Java 从哈希表中读取值显示了与自然顺序相反的结果

Java 从哈希表中读取值显示了与自然顺序相反的结果,java,collections,Java,Collections,为什么在执行以下代码时,HashMap和Hashtable具有不同的读取值行为,因为它们都基于相同的hashcode方法(整数hashcode)来存储值,这两种结构之间的唯一区别是:线程安全和允许空键和值: public static void main(String[] args) { Hashtable<Integer, String> hashtable = new Hashtable<>(); hashtable.put(4,"4"

为什么在执行以下代码时,HashMap和Hashtable具有不同的读取值行为,因为它们都基于相同的hashcode方法(整数hashcode)来存储值,这两种结构之间的唯一区别是:线程安全和允许空键和值:

public static void main(String[] args) {
        Hashtable<Integer, String> hashtable = new Hashtable<>();
        hashtable.put(4,"4");
        hashtable.put(2,"2");
        hashtable.put(3,"3");
        hashtable.put(8,"8");
        System.out.println(hashtable.values());// [8, 4, 3, 2]

        HashMap<Integer, String> hashMap = new HashMap<>();
        hashMap.put(4,"4");
        hashMap.put(2,"2");
        hashMap.put(3,"3");
        hashMap.put(8,"8");
        System.out.println(hashMap.values());// [2, 3, 4, 8]
    }
publicstaticvoidmain(字符串[]args){
Hashtable Hashtable=新的Hashtable();
hashtable.put(4,“4”);
hashtable.put(2,“2”);
hashtable.put(3,“3”);
hashtable.put(8,“8”);
System.out.println(hashtable.values());//[8,4,3,2]
HashMap HashMap=新的HashMap();
hashMap.put(4,“4”);
hashMap.put(2,“2”);
hashMap.put(3,“3”);
hashMap.put(8,“8”);
System.out.println(hashMap.values());//[2,3,4,8]
}

注意:这不是文档记录的行为,在另一个JVM版本/更新或JVM实现中可能会有所不同。话虽如此,它不太可能有所不同,因为可能有一些程序依赖于这种行为,并且不太可能有很好的理由更改默认实现


它在Oracle/OpenJDK中以自然顺序出现在HashMap中的原因是,对于较小的
hashCode()
值,键恰好没有随机排列在底层数组中。对于小于容量的非负值,它不会被简单的位重新排列函数“搅动”,哈希代码的值也是数组中的索引

HashMap的迭代器从数组的开始一直到结束

在哈希表的情况下,布局是相同的,不同之处在于
values()
从数组的末尾到开头进行迭代

    public boolean hasMoreElements() {
        Entry<?,?> e = entry;
        int i = index;
        Entry<?,?>[] t = table;
        /* Use locals for faster loop iteration */
        while (e == null && i > 0) {
            e = t[--i];
        }
        entry = e;
        index = i;
        return e != null;
    }
public boolean hasMoreElements(){
条目e=条目;
int i=指数;
条目[]t=表格;
/*使用局部变量加快循环迭代*/
而(e==null&&i>0){
e=t[--i];
}
条目=e;
指数=i;
返回e!=null;
}
我怀疑这是一个微优化,迭代器不需要与大小进行比较,而是
0


简言之,它既不支持线程安全,也不允许使用初始容量。(容量只与它所适用的值的范围有关)

注意:这不是文档化的行为,在另一个JVM版本/更新或JVM实现中可能会有所不同。话虽如此,它不太可能有所不同,因为可能有一些程序依赖于这种行为,并且不太可能有很好的理由更改默认实现


它在Oracle/OpenJDK中以自然顺序出现在HashMap中的原因是,对于较小的
hashCode()
值,键恰好没有随机排列在底层数组中。对于小于容量的非负值,它不会被简单的位重新排列函数“搅动”,哈希代码的值也是数组中的索引

HashMap的迭代器从数组的开始一直到结束

在哈希表的情况下,布局是相同的,不同之处在于
values()
从数组的末尾到开头进行迭代

    public boolean hasMoreElements() {
        Entry<?,?> e = entry;
        int i = index;
        Entry<?,?>[] t = table;
        /* Use locals for faster loop iteration */
        while (e == null && i > 0) {
            e = t[--i];
        }
        entry = e;
        index = i;
        return e != null;
    }
public boolean hasMoreElements(){
条目e=条目;
int i=指数;
条目[]t=表格;
/*使用局部变量加快循环迭代*/
而(e==null&&i>0){
e=t[--i];
}
条目=e;
指数=i;
返回e!=null;
}
我怀疑这是一个微优化,迭代器不需要与大小进行比较,而是
0


简言之,它既不支持线程安全,也不允许使用初始容量。(容量只与它所适用的值的范围有关)

HashMap/HashTable不保证任何特定的迭代顺序。“此类不保证映射的顺序;特别是,它不保证随着时间的推移顺序保持不变。”-@Eugene我正在准备一个java测试面试,他们询问hashmap和hashtable之间的区别。我只是问他们为什么会有不同的获取值的行为。这是一个愚蠢的问题,只要引用文档就足够了,如果你知道更多的话-好的,如果不是一个大的dealHashMap/哈希表不保证任何特定的迭代顺序。“此类不保证地图的顺序;特别是,它不能保证订单在一段时间内保持不变。“-@Eugene我正在准备一个java测试面试,他们询问hashmap和hashtable之间的区别。我只是问他们为什么会有不同的获取价值的行为。这是一个愚蠢的问题,只要引用文档就足够了,如果你知道的更多——好,如果不是大交易的话——当你说程序依赖于此,他们不会改变它的时候<8中的代码>哈希映射已损坏that@EugeneSun/Oracle在未记录的行为方面做出了突破性的改变,但他们将其保持在最低限度。关于OP的问题,对于Java1.2到9(我还没有测试过10)来说是正确的,当你说程序依赖于它,它们不会改变它<8中的代码>哈希映射已损坏that@EugeneSun/Oracle在未记录的行为方面做出了突破性的改变,但他们将其保持在最低限度。就OP的问题而言,Java1.2到9也是如此(我还没有测试10)