Java 7 中 ConcurrentHashMap 的存储结构如上图,ConcurrnetHashMap 由很多个 Segment 组合,而每一个 Segment 是一个类似于 HashMap 的结构,所以每一个 HashMap 的内部可以进行扩容。但是 Segment 的个数一旦初始化就不能改变,默认 Segment 的个数是 16 个,你也可以认为 ConcurrentHashMap 默认支持最多 16 个线程并发。
计算key应该放在哪个segment的哪个HashEntry元素中,然后在这个元素上执行查找操作
参考资料https://blog.csdn.net/ZOKEKAI/article/details/90051567 在ConcurrentHashMap中是以Node数组来保存map的键值对,同一个数组位置是用链表加红黑树来保存。但是这个数组只有在第一次增加元素的时候才会初始化。第一次增加元素的时候,默认初期长度是16. 当增加元素出现hash冲突的时候,优先以链表的形式存放。如果链表的长度已经超过了8,但是Node数组的长度小于64时,扩容数组;如果链表长度超过8,Node数组的长度也大于等于64,那就把链表转化为红黑树。
在get和put的过程中,计算下标时,首先得到元素的hashCode,然后把hashcode右移16位跟原来的hashcode逐位异或,最后跟n-1逐位与,得到下标。如下图所示:
来自https://blog.csdn.net/h1458280799/article/details/85265135 #1.8版本HashMap和1.8版本ConcurrentHashMap公共内容
通过扩容的方式把数组的节点分散开来,然后将这些元素复制到扩容后的新数组中。因为长度是扩展为原来的2倍,所以元素的位置要么在原来的位置,要么在原位置加上原来数组长度的位置。扩容后,hash值也得重新计算,新的hash值比旧的hash值增加了一位。所以判断位置的时候只需看新的hash值新增的数据位是1还是0.如果是1的话,那就放到原位置加上数组长度的位置,否则就是原位置。
扩容的核心方法是grow方法,这个方法的参数是minCapacity,表示新的数组需要存放元素的个数。首先计算出新的容量为旧容量的1.5倍,如果新容量比minCapacity要小,那么新的容量就为minCapacity。如果新容量大于Integer.MAX_VALUE-8,如果minCapacity>Integer.MAX_VALUE-8,那么新容量为Integer.MAX_VALUE,如果minCapacity<Integer.MAX_VALUE-8,那么新容量就是Integer.MAX_VALUE-8.
/** * 要分配的最大数组大小 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * ArrayList扩容的核心方法。 */ private void grow(int minCapacity) { // oldCapacity为旧容量,newCapacity为新容量 int oldCapacity = elementData.length; //将oldCapacity 右移一位,其效果相当于oldCapacity /2, //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍, int newCapacity = oldCapacity + (oldCapacity >> 1); //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量, if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE, //如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); //对minCapacity和MAX_ARRAY_SIZE进行比较 //若minCapacity大,将Integer.MAX_VALUE作为新数组的大小 //若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小 //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }通过list.ensureCapacity(Num)来调用,将ArrayList底层的数组的最小长度设置为大于Num(通过grow函数来扩容),以减少候选存放数据时扩容的次数。
Vector类的所有方法都是同步的。可以由两个线程安全地访问同一个Vector对象,但是一个线程访问Vector的话要在同步操作上耗费大量的时间。 ArrayList不是同步的,所以不需要保证线程安全的时候建议使用ArrayList。