使用 HashMap 时的奇怪行为

Weird Behaviour while using HashMap

我正在尝试修复代码,但我无法在此处 post,因此我在此处提供了精简版。

我在使用 HashMap 时得到不稳定的输出。

HashMap<Integer, String> test= new HashMap<>(); 
test.put(1, "one");
test.put(2, "one");
test.put(3, "one"); 
test.put(4,"four");
test.put(5, "one");
test.put(6, "one");
test.put(10, "one");
test.put(19, "one");    
test.put(20, "Sixteen");    
System.out.println(test);


HashMap<Integer, String> test3= new HashMap<>(200); 
test3.put(1, "one");
test3.put(2, "one");
test3.put(3, "one");    
test3.put(4,"four");
test3.put(5, "one");
test3.put(6, "one");
test3.put(10, "one");
test3.put(19, "one");   
test3.put(20, "Sixteen");
System.out.println(test3);  

产出

test --> {1=one, 19=one, 2=one, 3=one, 4=four, 20=Sixteen, 5=one, 6=one, 10=one}
test3--> {1=one, 2=one, 3=one, 4=four, 5=one, 6=one, 10=one, 19=one, 20=Sixteen}---> My desired output. 

为什么输入值相同,结果却不同。这种排序有何不同,即元素的存储?

我无法使用第二种方法,因为尺寸是动态的,它会根据应用程序不断变化。我可以使用相同的 TreeMap 并获得所有值的一致输出吗?

那是因为

new HashMap<>() Constructs an empty HashMap with the default initial capacity (16)

因此它倾向于打印索引 0--->15 的值,然后再次从索引 16-->31 打印为 0--->15。

来自 Java docs 条目 HashMap class,

This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.

为什么大小不同输出结果不同 –

这是因为在调用hashmap的put方法时内部调用了indexFor(hash,table.length)。 Table.length 不同,这意味着默认值为 16,但您的第二种方式大小为 200。因此索引会有所不同。

请阅读更多内容article

我可以使用同样的 TreeMap 并获得所有值的一致输出吗?

在 Treemap 保证中,键将被排序,因此您将得到 {1=one, 2=one, 3=one, 4=four, 5=one, 6=one, 10=one, 19=one, 20=Sixteen}

如果您想按插入的顺序检索,您可以使用 LinkedHashmap

HashMap 不作为订单给予保证。如果你想根据你插入的顺序检索地图中的元素,你可以使用 LinkedHashMap.

HashMaps 有一个叫做负载因子的概念。负载因子是 hashmap 在调整自身大小之前可以达到的程度。如果负载因子为 0.5(或 50%),那么当 hashmap 达到 50% 的容量时,它将调整自身大小以为更多元素腾出空间。

hashmap 的加载因子通常小于 100% 的原因是元素的存储方式。当您生成散列时,您使用散列函数。您使用的散列函数是默认值,它基于正在存储的对象的 equals() 方法以及一些目前并不重要的额外操作。问题是,两个元素可以以相同的散列结尾,但不能保证是唯一的。当您尝试使用相同的散列值存储两个值时,它们最终会出现在散列图中的相同 'bucket' 中。这称为碰撞。当你有冲突时,hashmap 需要一个策略来处理它。

有时策略是'linear probing'。这意味着,如果一个元素发生冲突,hashmap 将遍历接下来的几个桶以寻找空桶。

有时策略是 'chaining',如果一个元素发生冲突,hashmap 将用链表替换现有元素,并将每个发生冲突的元素放入列表中。

所有这一切意味着当元素在 hashmap 中发生冲突时,插入和检索元素变得更慢。因此,为了减少冲突的机会,hashmap 根据负载因子调整自身大小。

最重要的是,正如其他人所提到的,在基本的 HashMap 中不能保证排序。您需要使用像 LinkedHashMap 这样的实现。

如果您希望键自然排序,请使用 TreeMap

The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.

Map<Integer, String> test= new TreeMap<>(); 
test.put(1, "one");
test.put(2, "one");
test.put(3, "one"); 
test.put(4,"four");
test.put(5, "one");
test.put(6, "one");
test.put(10, "one");
test.put(19, "one");    
test.put(20, "Sixteen");    
System.out.println(test); // Output : {1=one, 2=one, 3=one, 4=four, 5=one, 6=one, 10=one, 19=one, 20=Sixteen}

具有默认初始容量 (16) 的 HashMap

{1=一,2=一,3=一,4=四,5=一,6=一,10=一}

{1=一,2=一,3=一,4=四,5=一,6=一,10=一}