使用Thread时HashMap顺序会更改,但在没有Thread的情况下是常量

Meh*_*ran 10 java multithreading hashmap

我知道HashMap 不保证订单.请考虑以下代码:

import java.util.HashMap;
import java.util.Map;

public class SandBox {
    protected static class Book {
        String name;

        public Book(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    protected static class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            final int n = 10;
            Book[] books = new Book[n];
            for (int i=0; i<n; i++)
                books[i] = new Book("b" + i);
            for (Book b : books)
                System.out.print(b + ", ");
            System.out.println();
            HashMap<Book, Object> hm = new HashMap<>();
            for (Book b : books)
                hm.put(b, null);
            for (Map.Entry<Book, Object> entry : hm.entrySet())
                System.out.print(entry.getKey() + ", ");
            System.out.println();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread t = new MyThread();
        t.start();
        t.join();
    }
}
Run Code Online (Sandbox Code Playgroud)

在每次运行中,HashMap的顺序是不同的(如预期的那样).例如:

输出#1:

b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, 
b3, b4, b7, b9, b0, b8, b1, b2, b6, b5,
Run Code Online (Sandbox Code Playgroud)

输出#2:

b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, 
b9, b4, b3, b7, b8, b0, b1, b5, b6, b2,
Run Code Online (Sandbox Code Playgroud)

但奇怪的是,如果我更换线路

t.start();
t.join();
Run Code Online (Sandbox Code Playgroud)

t.run();
Run Code Online (Sandbox Code Playgroud)

(不使用多线程)输出始终相同:

b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, 
b0, b3, b7, b4, b2, b6, b9, b1, b5, b8, 
Run Code Online (Sandbox Code Playgroud)

我不明白HashMap的顺序和Thread之间的关系.有人可以向我解释为什么会这样吗?

小智 14

这是因为HashMap内部的顺序将取决于哈希码的实现.

您的Book类没有实现,hashCode因此它将使用默认类

尽可能合理,Object类定义的hashCode方法确实为不同的对象返回不同的整数.(这通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言不需要此实现技术.)

这意味着它将使用内存地址.

在您的情况下,对于单个线程,重新运行时分配的内存地址是相同的,这在线程版本中不是这种情况.

但这只是"偶然",即使在单线程中你也不能依赖它(别人会运行它并获得不同的结果,或者甚至当你以后运行它时你可以得到不同的结果,因为对象将有不同的内存地址)

在使用对象时,请务必覆盖hashCode(&equals)hashmap.

  • *"在hashmap中使用对象时,请始终覆盖hashCode(&equals)."* - 这不是一个好建议.如果对象需要等同于标识语义,则应该重写Object :: hashCode**NOT**. (5认同)
  • @StephenC是的,但是如果你想依赖HashMap中的身份语义,你通常应该使用`java.util.IdentityHashMap`,而不是强制该对象不覆盖hashCode. (4认同)
  • 像IntelliJ IDEA这样的IDE会为你生成hashCode方法,只需点击alt-insert并选择hashcode即可. (2认同)
  • _这意味着它将使用内存地址._默认情况下,HotSpot不是这样.它使用随机数生成器生成标识hashCode并将其存储在对象标头中. (2认同)