You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于Hashtable与HashMap的keySet()遍历顺序的疑问

HashMap与Hashtable遍历顺序的疑问解析

问题背景

我最近学习Java时发现,HashMap和Hashtable理论上不会保留插入顺序或键的顺序,但测试中出现了例外情况。

测试代码

import java.util.HashMap;
import java.util.Hashtable;

public class Main {
    public static void main(String[] args) {

        Hashtable<Integer, Integer> hashtableInt = new Hashtable<>();
        Hashtable<String, Integer> hashtableStr = new Hashtable<>();
        HashMap<Integer, Integer> hashMapInt = new HashMap<>();
        HashMap<String, Integer> hashMapStr = new HashMap<>();

        for (int i = 0; i < 25; i++) {
            hashtableInt.put(i, i);
            hashtableStr.put(String.valueOf(i), i);
            hashMapInt.put(i, i);
            hashMapStr.put(String.valueOf(i), i);
        }

        System.out.println("Hashtable with Integer keys : ");
        for (int key : hashtableInt.keySet())   System.out.print(key + " ");

        System.out.println("\n\nHashtable with String keys : ");
        for (String key : hashtableStr.keySet())   System.out.print(key + " ");

        System.out.println("\n\nHashMap with Integer keys : ");
        for (int key : hashMapInt.keySet())   System.out.print(key + " ");

        System.out.println("\n\nHashMap with String keys : ");
        for (String key : hashMapStr.keySet())   System.out.print(key + " ");
    }
}

输出结果

Hashtable with Integer keys : 
24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 

Hashtable with String keys : 
19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 24 3 23 2 22 1 21 0 20 

HashMap with Integer keys : 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 

HashMap with String keys : 
22 23 24 10 11 12 13 14 15 16 17 18 19 0 1 2 3 4 5 6 7 8 9 20 21 

从输出可见:使用Integer作为键的哈希表存在特定顺序,仅使用String作为键的哈希表无固定顺序,且Hashtable与HashMap的Integer键遍历顺序相反。

疑问

  • 为什么Hashtable.keySet()和HashMap.keySet()会出现特定顺序?通常哈希表和集合是无特定顺序的,我是否遗漏了什么?
  • 为什么Hashtable.keySet()和HashMap.keySet()的顺序相反?两者都是哈希实现,内部机制有何不同?
  • 若keySet()存在顺序,为何使用String键时又会出现无规律的情况?

解答

1. Integer键出现特定顺序的原因

哈希表的遍历顺序本质是内部数组的遍历顺序——哈希表会根据键的哈希值计算数组下标,将键值对存入对应槽位,遍历则按数组槽位的顺序进行。

对于Integer类型,它的hashCode()直接返回自身数值:

public int hashCode() {
    return Integer.hashCode(value);
}
// Integer.hashCode的实现为return i;

当插入连续的Integer值(0到24)时,它们的哈希值也连续,计算后的数组下标会呈现连续分布。此时遍历数组槽位,就会呈现出类似有序的结果。但要明确:这种顺序是哈希表内部实现的副作用,并非API承诺的行为。JDK官方文档明确说明,HashMap/Hashtable的keySet遍历顺序不固定,也不保证与插入顺序或键的自然顺序一致。一旦JDK版本更新、哈希表扩容策略变化,或插入的Integer值不连续,这种“假顺序”就会消失。

2. Hashtable与HashMap遍历顺序相反的原因

两者的内部实现细节差异导致了顺序相反:

  • HashMap:内部数组容量默认是2的幂,哈希下标通过hash & (length-1)计算,遍历从数组下标0开始到末尾。连续的Integer值对应连续的小下标,因此遍历顺序是0到24。
  • Hashtable:内部数组容量默认是11(扩容规则为2*length+1,始终是奇数),哈希下标通过hash % length计算(哈希值由hashCode() ^ (hashCode() >>> 15)生成),且链表采用头插法。这些细节导致大的Integer值对应的数组下标更靠前,加上遍历逻辑的差异,最终呈现逆序遍历结果。

核心是:两者的哈希函数、扩容策略、链表插入方式均不同,这些内部细节导致了遍历顺序的差异,但都不属于API承诺的行为,只是特定场景下的巧合。

3. String键顺序无规律的原因

String的hashCode()基于字符序列计算,并非简单的自身值:

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

不同字符串的哈希值分布无规律,计算后的数组下标会随机分散在数组的各个槽位,遍历自然呈现杂乱无章的顺序,这才是哈希表遍历的正常表现。


内容的提问来源于stack exchange,提问作者CheongJu Not Apple

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 05:55:31