Java 2-D(并发)哈希映射:2属性密钥类型?hashmap的hashmap?[更新]
所以我需要一个二维的Java 2-D(并发)哈希映射:2属性密钥类型?hashmap的hashmap?[更新],java,performance,hashmap,concurrenthashmap,Java,Performance,Hashmap,Concurrenthashmap,所以我需要一个二维的ConcurrentHashMap 它必须尽可能快,因为我将非常频繁地添加和更新它的值。它位于多线程应用程序中,因此可以选择使用ConcurrentHashMap而不仅仅是HashMap “x”和“y”索引都是具有已知范围(0到40000000)的整数 我需要知道的是:什么是最有效的方式来实现这一点,以便它将尽可能快?最明显的方法是执行文字二维哈希映射: ConcurrentHashMap-foo 或者我可以创建一个具有两个属性x和y的私有类“IntPair”,并将其用作密钥
ConcurrentHashMap
它必须尽可能快,因为我将非常频繁地添加和更新它的值。它位于多线程应用程序中,因此可以选择使用ConcurrentHashMap而不仅仅是HashMap
“x”和“y”索引都是具有已知范围(0到40000000)的整数
我需要知道的是:什么是最有效的方式来实现这一点,以便它将尽可能快?最明显的方法是执行文字二维哈希映射:
ConcurrentHashMap-foo代码>
或者我可以创建一个具有两个属性x和y的私有类“IntPair”,并将其用作密钥。。。如果我这样做,那么做equals()
和hashcode()
最有效的方法是什么?我会分配太多新的IntPair
s吗?我是否可以为分配的每个x/y保留一组IntPair
s,然后使用纯自反的equals(),以便只检查完全相同的对象实例
更新:
现在我仔细研究了Integer.valueOf(int),它使用的特定缓存模型在这里没有意义,因为我处理的是一个非常稀疏的矩阵,其中包含不可预测的条目。我真的需要缓存所有使用的整数对,而不是预先指定的子集
直观地说,在我看来,在一个大地图中查找一个IntPair来查看我是否已经创建了它,实际上,与在大的“2-D”ConcurrentHashMap中查找差不多,不是吗?因此,这里的解决方案似乎就是每次查找密钥时都使用新的IntPair(x,y)
。是吗?ConcurrentHashMap
相当大,因此您可能不需要它们的集合
短期对象实际上分配速度非常快。你还需要创建整数吗
您可以对坐标对象进行内部搜索,但仅进行一次查找的成本可能与创建坐标对象的成本相当。Integer
的真正优势在于,当您将大量实例保留一段时间后,相同的实例将被共享
如果性能确实是一个大问题,那么您可以编写(或使用)一个映射类型对象,将long映射到引用。如果看到自定义地图也具有与坐标系相关的功能(如查找最近的或在某个范围内的),我不会感到惊讶。ConcurrentHashMap
相当大,因此您可能不需要它们的集合
短期对象实际上分配速度非常快。你还需要创建整数吗
您可以对坐标对象进行内部搜索,但仅进行一次查找的成本可能与创建坐标对象的成本相当。Integer
的真正优势在于,当您将大量实例保留一段时间后,相同的实例将被共享
如果性能确实是一个大问题,那么您可以编写(或使用)一个映射类型对象,将long映射到引用。如果看到自定义地图也有与坐标系相关的功能(如查找最近的或在某个范围内的),我不会感到惊讶。这取决于(x,y)点在40000000 x 40000000矩阵中的稀疏程度。我的猜测是,矩阵无论如何都会非常稀疏,因此创建大量的ConcurrentHashMap
s将非常昂贵
相比之下,您的(不可变的)IntPair
建议似乎更有吸引力。正如您所建议的,您甚至可以缓存其中的一些对以提高性能(请参阅以了解如何使用静态嵌套类和静态工厂方法实现这一点)。因为hashcode总是必需的,所以您可以在构造函数中预先计算它,并将其保存为最终字段。要计算相等,可以使用缓存中对象的标识相等,否则需要分别比较x和y
编辑:这是的(OpenJDK)。它取决于(x,y)点在40000000 x 40000000矩阵中的稀疏程度。我的猜测是,矩阵无论如何都会非常稀疏,因此创建大量的ConcurrentHashMap
s将非常昂贵
相比之下,您的(不可变的)IntPair
建议似乎更有吸引力。正如您所建议的,您甚至可以缓存其中的一些对以提高性能(请参阅以了解如何使用静态嵌套类和静态工厂方法实现这一点)。因为hashcode总是必需的,所以您可以在构造函数中预先计算它,并将其保存为最终字段。要计算相等,可以使用缓存中对象的标识相等,否则需要分别比较x和y
编辑:这是的(OpenJDK)。对于Zach,是的,矩阵将非常稀疏
我看了你链接的页面,毫无疑问Integer.valueOf(int)的功能将是理想的。如果我在我的IntPair
类中开发了一个类似的静态方法,我可以假设我可以定义equals()
来只检查严格的自反等式吗
也就是说,我看不到在该页面中,它解释了如何使用静态嵌套类和静态工厂方法实现该功能。。。。不知怎么的,我是不是错过了?我该怎么做
谢谢 作为对扎克的回应,是的,矩阵将非常稀疏
我看了你链接的页面,毫无疑问Integer.valueOf(int)的功能将是理想的。如果我在我的IntPair
类中开发了一个类似的静态方法,我可以假设我可以定义equals()
来只检查严格的自反等式吗
也就是说,我看不到在那个页面中它解释了如何使用静态嵌套类和静态
import java.io.*;
import java.util.*;
public class Int2DMap implements Map, Serializable {
private static final int DEFAULT_INITIAL_CAPACITY = 16;
private static final int MAXIMUM_CAPACITY = 1 << 30;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
protected Entry[] table;
protected int size;
protected int threshold;
protected float loadFactor;
protected transient volatile int modCount;
public Int2DMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
// Find a power of 2 >= initialCapacity
int capacity = 1;
while (capacity < initialCapacity) {
capacity <<= 1;
}
this.loadFactor = loadFactor;
this.threshold = (int) (capacity * loadFactor);
this.table = new Entry[capacity];
}
public Int2DMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public Int2DMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
public boolean containsKey(Object key) {
int[] xy = (int[]) key;
return containsKey(xy[0], xy[1]);
}
public Object get(Object key) {
int[] xy = (int[]) key;
return get(xy[0], xy[1]);
}
public Object put(Object key, Object value) {
int[] xy = (int[]) key;
return put(xy[0], xy[1], value);
}
public Object remove(Object key) {
int[] xy = (int[]) key;
return remove(xy[0], xy[1]);
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
protected static final int indexFor(int x, int y, int length) {
return (x * 31 + y) & (length - 1);
}
public Object get(int x, int y) {
for (Entry e = table[indexFor(x, y, table.length)]; e != null; e = e.next) {
if (e.x == x && e.y == y) {
return e.value;
}
}
return null;
}
public boolean containsKey(int x, int y) {
return getEntry(x, y) != null;
}
protected Entry getEntry(int x, int y) {
for (Entry e = table[indexFor(x, y, table.length)]; e != null; e = e.next) {
if (e.x == x && e.y == y) {
return e;
}
}
return null;
}
public Object put(int x, int y, Object value) {
int i = indexFor(x, y, table.length);
for (Entry e = table[i]; e != null; e = e.next) {
if (e.x == x && e.y == y) {
Object oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(x, y, value, i);
return null;
}
protected void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int) (newCapacity * loadFactor);
}
protected void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry e = src[j];
if (e != null) {
src[j] = null;
do {
Entry next = e.next;
int i = indexFor(e.x, e.y, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
public void putAll(Map m) {
int numKeysToBeAdded = m.size();
if (numKeysToBeAdded == 0) {
return;
}
if (numKeysToBeAdded > threshold) {
int targetCapacity = (int) (numKeysToBeAdded / loadFactor + 1);
if (targetCapacity > MAXIMUM_CAPACITY)
targetCapacity = MAXIMUM_CAPACITY;
int newCapacity = table.length;
while (newCapacity < targetCapacity)
newCapacity <<= 1;
if (newCapacity > table.length)
resize(newCapacity);
}
for (Iterator i = m.entrySet().iterator(); i.hasNext();) {
Map.Entry e = (Map.Entry) i.next();
put(e.getKey(), e.getValue());
}
}
public Object remove(int x, int y) {
Entry e = removeEntryForKey(x, y);
return (e == null ? null : e.value);
}
protected Entry removeEntryForKey(int x, int y) {
int i = indexFor(x, y, table.length);
Entry prev = table[i];
Entry e = prev;
while (e != null) {
Entry next = e.next;
Object k;
if (e.x == x && e.y == y) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
protected Entry removeMapping(Object o) {
if (!(o instanceof Entry))
return null;
Entry entry = (Entry) o;
int x = entry.x;
int y = entry.y;
int i = indexFor(x, y, table.length);
Entry prev = table[i];
Entry e = prev;
while (e != null) {
Entry next = e.next;
if (e.x == x && e.y == y) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
public void clear() {
modCount++;
Entry[] tab = table;
for (int i = 0; i < tab.length; i++)
tab[i] = null;
size = 0;
}
public boolean containsValue(Object value) {
Entry[] tab = table;
for (int i = 0; i < tab.length; i++)
for (Entry e = tab[i]; e != null; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
static class Entry implements Map.Entry {
final int x;
final int y;
Object value;
Entry next;
Entry(int x, int y, Object value, Entry next) {
this.x = x;
this.y = y;
this.value = value;
this.next = next;
}
public final Object getKey() {
return new int[] { x, y };
}
public final Object getValue() {
return value;
}
public final Object setValue(Object newValue) {
Object oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry) o;
int[] xy = (int[])e.getKey();
if (x == xy[0] && y == xy[1]) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public final int hashCode() {
return ((31 + x) * 31 + y);
}
public final String toString() {
return "[" + x + ", " + y + "]=" + value;
}
/**
* This method is invoked whenever the value in an entry is overwritten by
* an invocation of put(k,v) for a key k that's already in the HashMap.
*/
void recordAccess(Int2DMap m) {
}
/**
* This method is invoked whenever the entry is removed from the table.
*/
void recordRemoval(Int2DMap m) {
}
}
void addEntry(int x, int y, Object value, int bucketIndex) {
Entry e = table[bucketIndex];
table[bucketIndex] = new Entry(x, y, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
private abstract class HashIterator implements Iterator {
Entry next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
Entry current; // current entry
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
final Entry nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry e = current = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
return e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
int x = current.x;
int y = current.y;
current = null;
Int2DMap.this.removeEntryForKey(x, y);
expectedModCount = modCount;
}
}
private final class ValueIterator extends HashIterator {
public Object next() {
return nextEntry().value;
}
}
private final class KeyIterator extends HashIterator {
public Object next() {
return nextEntry().getKey();
}
}
private final class EntryIterator extends HashIterator {
public Map.Entry next() {
return nextEntry();
}
}
// Subclass overrides these to alter behavior of views' iterator() method
Iterator newKeyIterator() {
return new KeyIterator();
}
Iterator newValueIterator() {
return new ValueIterator();
}
Iterator newEntryIterator() {
return new EntryIterator();
}
public Set keySet() {
return new KeySet();
}
private final class KeySet extends AbstractSet {
public Iterator iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
int[] xy = (int[]) o;
return Int2DMap.this.removeEntryForKey(xy[0], xy[1]) != null;
}
public void clear() {
Int2DMap.this.clear();
}
}
public Collection values() {
return new Values();
}
private final class Values extends AbstractCollection {
public Iterator iterator() {
return newValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
Int2DMap.this.clear();
}
}
public Set entrySet() {
return new EntrySet();
}
private final class EntrySet extends AbstractSet {
public Iterator iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Entry e = (Entry) o;
Entry candidate = getEntry(e.x, e.y);
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
return removeMapping(o) != null;
}
public int size() {
return size;
}
public void clear() {
Int2DMap.this.clear();
}
}
public static void main(String[] args) {
try {
Int2DMap map = new Int2DMap();
map.put(20, 6000, "Test");
System.out.println(map.size() == 1);
System.out.println(map.get(20, 6000) != null);
System.out.println("Test".equals(map.get(20, 6000)));
for (Iterator iter = map.values().iterator(); iter.hasNext();) {
System.out.println("Test".equals(iter.next()));
}
for (Iterator iter = map.keySet().iterator(); iter.hasNext();) {
int[] key = (int[])iter.next();
System.out.println(key[0] == 20 && key[1] == 6000);
}
for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
Map.Entry e = (Map.Entry)iter.next();
System.out.println(e.toString().equals("[20, 6000]=Test"));
}
map.remove(20, 6000);
System.out.println(map.size() == 0 && map.get(20, 6000) == null);
long start = System.nanoTime();
int max = 40000000;
for (int i = 0; i < 500000; i++) {
int x = (int)(Math.random() * max);
int y = (int)(Math.random() * max);
map.put(x, y, "");
int x2 = (int)(Math.random() * max);
int y2 = (int)(Math.random() * max);
Object o = map.get(x2, y2);
}
System.out.println(map.size());
System.out.println((System.nanoTime() - start) / 1000000);
Map map2 = new HashMap();
start = System.nanoTime();
for (int i = 0; i < 500000; i++) {
String key = "" + (int)(Math.random() * max) + "," + (int)(Math.random() * max);
map2.put(key, "");
String key2 = "" + (int)(Math.random() * max) + "," + (int)(Math.random() * max);
Object o = map2.get(key2);
}
System.out.println(map2.size());
System.out.println((System.nanoTime() - start) / 1000000);
} catch (Throwable t) {
t.printStackTrace();
}
}
}