Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java HashMap性能优化/备选方案_Java_Performance_Optimization_Map_Hashmap - Fatal编程技术网

Java HashMap性能优化/备选方案

Java HashMap性能优化/备选方案,java,performance,optimization,map,hashmap,Java,Performance,Optimization,Map,Hashmap,我想创建一个大的HashMap,但是put()性能不够好。有什么想法吗 欢迎提供其他数据结构建议,但我需要Java地图的查找功能: map.get(键) 在我的例子中,我想创建一个包含2600万条目的地图。使用标准的Java HashMap,插入200-300万次之后,put速度变得慢得令人无法忍受 还有,有人知道对密钥使用不同的散列码分布是否有帮助吗 我的hashcode方法: byte[] a = new byte[2]; byte[] b = new byte[3]; ... publi

我想创建一个大的HashMap,但是
put()
性能不够好。有什么想法吗

欢迎提供其他数据结构建议,但我需要Java地图的查找功能:

map.get(键)

在我的例子中,我想创建一个包含2600万条目的地图。使用标准的Java HashMap,插入200-300万次之后,put速度变得慢得令人无法忍受

还有,有人知道对密钥使用不同的散列码分布是否有帮助吗

我的hashcode方法:

byte[] a = new byte[2];
byte[] b = new byte[3];
...

public int hashCode() {
    int hash = 503;
    hash = hash * 5381 + (a[0] + a[1]);
    hash = hash * 5381 + (b[0] + b[1] + b[2]);
    return hash;
}
我使用加法的关联属性来确保相等的对象具有相同的哈希代码。数组是值在0-51范围内的字节。值在任一数组中仅使用一次。如果a数组包含相同的值(按任意顺序),而b数组包含相同的值,则对象是相等的。所以a={0,1}b={45,12,33}和a={1,0}b={33,45,12}是相等的

编辑,一些注释:

  • 一些人批评使用散列图或其他数据结构来存储2600万个条目。我不明白为什么这会显得奇怪。在我看来,这是一个典型的数据结构和算法问题。我有2600万个条目,我希望能够快速地将它们插入数据结构并从数据结构中查找它们:给我数据结构和算法

  • 将默认Java HashMap的初始容量设置为2600万会降低性能

  • 有些人建议在其他情况下使用数据库,这绝对是明智的选择。但我真的在问一个数据结构和算法的问题,一个完整的数据库会比一个好的数据结构解决方案更具杀伤力,速度也慢得多(毕竟数据库只是软件,但会有通信和可能的磁盘开销)


HashMap具有初始容量,HashMap的性能非常依赖于生成底层对象的hashCode


尝试调整两者。

我的第一个想法是确保正确初始化HashMap。从:

HashMap实例有两个影响其性能的参数:初始容量和负载因子。容量是哈希表中的存储桶数,初始容量只是创建哈希表时的容量。负载因子是在自动增加哈希表容量之前允许哈希表达到的满度的度量。当哈希表中的条目数超过加载因子和当前容量的乘积时,哈希表将被重新格式化(即,重建内部数据结构),以便哈希表的存储桶数大约是存储桶数的两倍


因此,如果您从一个太小的HashMap开始,那么每次它需要调整大小时,all都会重新计算HashMap。。。这可能就是你到达200-300万个插入点时的感受。

在开始时分配一个大地图。如果您知道它将有2600万个条目,并且您有足够的内存,请执行一个新的HashMap(30000000)

你确定你有足够的内存来存储2600万个条目和2600万个键和值吗?对我来说,这听起来像是很多回忆。你确定垃圾收集在你的200万到300万大关上还不错吗?我可以想象这是一个瓶颈。

你可以尝试两件事:

  • 使您的
    hashCode
    方法返回更简单、更有效的内容,例如连续整数

  • 将地图初始化为:

    Map map = new HashMap( 30000000, .95f );
    
这两个动作将极大地减少结构正在进行的重新灰化量,我认为这很容易测试

如果这不起作用,考虑使用不同的存储这样的RDBMS。

编辑

奇怪的是,在您的情况下,设置初始容量会降低性能

请参阅:

如果初始容量大于最大入口数除以负载系数,则不会发生再灰化操作

我做了一个微地图(它不是绝对确定的,但至少证明了这一点)

使用此类具有上一个程序的密钥

 map.put( new Item() , i );
给我:

real     0m11.188s
user     0m10.784s
sys 0m0.261s


real     0m9.348s
user     0m9.071s
sys  0m0.161s

详细介绍Pascal:您了解HashMap是如何工作的吗?您的哈希表中有一些插槽。找到每个键的哈希值,然后映射到表中的一个条目。如果两个散列值映射到同一条目的“散列冲突”,则HashMap将构建一个链表

哈希冲突会破坏哈希映射的性能。在极端情况下,如果您的所有键都具有相同的哈希代码,或者如果它们具有不同的哈希代码,但它们都映射到同一个插槽,那么您的哈希映射将变成一个链接列表

因此,如果您看到性能问题,我要检查的第一件事是:我是否得到了散列码的随机分布?如果不是,则需要更好的哈希函数。在这种情况下,“更好”可能意味着“对我的特定数据集更好”。比如,假设您正在处理字符串,并将字符串的长度作为哈希值。(不是Java的String.hashCode是如何工作的,但我只是在编一个简单的例子。)如果字符串的长度变化很大,从1到10000,并且在该范围内分布相当均匀,那么这可能是一个非常好的哈希函数。但是如果字符串都是1或2个字符,这将是一个非常糟糕的哈希函数

编辑:我应该添加:每次添加新条目时,HashMap都会检查这是否重复。当出现哈希冲突时,它必须将传入密钥与映射到该插槽的每个密钥进行比较。因此,在最坏的情况下,所有内容都散列到一个插槽中,第二个键与第一个键进行比较,第三个键与#1和#2进行比较,第四个键与#1、2和#3进行比较,等等。当您到达#100万键时,您已经完成了超过万亿次的操作
 map.put( new Item() , i );
real     0m11.188s
user     0m10.784s
sys 0m0.261s


real     0m9.348s
user     0m9.071s
sys  0m0.161s
return a[0]+a[1]*52+b[0]*52*52+b[1]*52*52*52+b[2]*52*52*52*52;
public int hashCode() {
  if(this.hashCode == null) {
     this.hashCode = computeHashCode();
  }
  return this.hashCode;
}

private int computeHashCode() {
   int hash = 503;
   hash = hash * 5381 + (a[0] + a[1]);
   hash = hash * 5381 + (b[0] + b[1] + b[2]);
   return hash;
}
public int hashCode() {
    return 31 * Arrays.hashCode(a) + Arrays.hashCode(b);
}
hash = hash * 5381 + (c0*a[0] + c1*a[1]);
hash = hash * 5381 + (c0*b[0] + c1*b[1] + c3*b[2]);
    int code = a[0];
    code = (code << 6) + a[1];
    code = (code << 6) + b[0];
    code = (code << 6) + b[1];
    code = (code << 6) + b[2];
    return code;
    int code = a[0];
    code *= 51 + a[1];
    code *= 51 + b[0];
    code *= 51 + b[1];
    code *= 51 + b[2];
    return code;
public int hashCode() {       
    // assume that both a and b are sorted       
    return a[0] + powerOf52(a[1], 1) + powerOf52(b[0], 2) + powerOf52(b[1], 3) + powerOf52(b[2], 4);
}

public static int powerOf52(byte b, int power) {
    int result = b;
    for (int i = 0; i < power; i++) {
        result *= 52;
    }
    return result;
}
168350.17
109409.195
81344.91
64319.023
53780.79
45931.258
39680.29
34972.676
31354.514
28343.062
25562.371
23850.695
22299.22
20998.006
19797.799
18702.951
17702.434
16832.182
16084.52
15353.083
337837.84
337268.12
337078.66
336983.97
313873.2
317460.3
317748.5
320000.0
309704.06
310752.03
312944.5
265780.75
275540.5
264350.44
273522.97
270910.94
279008.7
276285.5
283455.16
289603.25
@Test
public void shouldOverwriteWhenEqualAndHashcodeSame() {
    String s = new String("ese");
    String ese = new String("ese");
    // same hash right?
    assertEquals(s.hashCode(), ese.hashCode());
    // same class
    assertEquals(s.getClass(), ese.getClass());
    // AND equal
    assertTrue(s.equals(ese));

    Map map = new HashMap();
    map.put(s, 1);
    map.put(ese, 2);
    SomeClass some = new SomeClass();
    // still  same hash right?
    assertEquals(s.hashCode(), ese.hashCode());
    assertEquals(s.hashCode(), some.hashCode());

    map.put(some, 3);
    // what would we get?
    assertEquals(2, map.size());

    assertEquals(2, map.get("ese"));
    assertEquals(3, map.get(some));

    assertTrue(s.equals(ese) && s.equals("ese"));
}

class SomeClass {
    public int hashCode() {
        return 100727;
    }
}
@Test
public void shouldInsertWhenNotEqualAndHashcodeSame() {
    MyString s = new MyString("ese");
    MyString ese = new MyString("ese");
    // same hash right?
    assertEquals(s.hashCode(), ese.hashCode());
    // same class
    assertEquals(s.getClass(), ese.getClass());
    // BUT not equal
    assertFalse(s.equals(ese));

    Map map = new HashMap();
    map.put(s, 1);
    map.put(ese, 2);
    SomeClass some = new SomeClass();
    // still  same hash right?
    assertEquals(s.hashCode(), ese.hashCode());
    assertEquals(s.hashCode(), some.hashCode());

    map.put(some, 3);
    // what would we get?
    assertEquals(3, map.size());

    assertEquals(1, map.get(s));
    assertEquals(2, map.get(ese));
    assertEquals(3, map.get(some));
}

/**
 * NOTE: equals is not overridden so the default implementation is used
 * which means objects are only equal if they're the same instance, whereas
 * the actual Java String class compares the value of its contents.
 */
class MyString {
    String i;

    MyString(String i) {
        this.i = i;
    }

    @Override
    public int hashCode() {
        return 100727;
    }
}
T[] array = new T[Key.maxHashCode];

void put(Key k, T value) {
    array[k.hashCode()] = value;

T get(Key k) {
    return array[k.hashCode()];
}
public int hashCode() {
    assert a[0] < a[1]; 
    int ahash = a[1] * a[1] / 2 
              + a[0];

    assert b[0] < b[1] && b[1] < b[2];

    int bhash = b[2] * b[2] * b[2] / 6
              + b[1] * b[1] / 2
              + b[0];
    return bhash * 52 * 52 / 2 + ahash;
}

static final int maxHashCode = 52 * 52 / 2 * 52 * 52 * 52 / 6;