1. ArrayList 和 LinkedList 都是非线程安全的集合。
为了解决这个问题,可以采取以下两种方式:
1. 优先在方法内使用,定义为局部变量:
如果将这些集合作为局部变量在方法内部使用,每个线程都会有自己的集合实例,因此不会出现线程安全问题。这种方式适用于集合的生命周期较短且仅在单个线程内使用的情况。
2. 在成员变量中使用时,使用线程安全的集合替代:
如果必须在成员变量中使用这些集合,可以通过以下方式确保线程安全:
ArrayList:可以使用 Collections.synchronizedList 方法将 ArrayList 转换为线程安全的集合
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
LinkedList:可以使用 ConcurrentLinkedQueue 替代 LinkedList,ConcurrentLinkedQueue 是一个线程安全的队列实现。
Queue<String> concurrentQueue = new ConcurrentLinkedQueue<>();
2. 为何 HashMap 的数组长度一定是 2 的次幂?
1. 计算索引时效率更高:
在 HashMap 中,元素的索引是通过 hash(key) & (length - 1) 计算得到的。如果数组长度是 2 的次幂,length - 1 的二进制表示会是一串连续的 1(例如,长度为 8 时,length - 1 的二进制是 0111)。
这样,hash(key) & (length - 1) 的效果等同于 hash(key) % length,但位运算的效率远高于取模运算,因此可以提高索引计算的效率。
2. 扩容时重新计算索引效率更高:
当 HashMap 扩容时,元素的索引需要重新计算。如果数组长度是 2 的次幂,可以通过以下方式快速判断元素在新数组中的位置:
如果 hash(key) & oldLength == 0,则元素在新数组中的位置与旧数组中的位置相同。
否则,元素在新数组中的位置为 旧位置 + 旧数组长度。
这种方式避免了重新计算所有元素的索引,提高了扩容时的效率。