java集合源码分析,对HashMap一步步进行debug分析。至于面试,集合的其他细节另外开一篇
文章目录
- java基础
- 一、List
- 1、ArrayList底层操作机制
- 2、Vector底层结构和机制
- 3、Vector 和 ArrayList 的比较
- 4、linkList底层机制
- 5、ArrayList 和 linkedList 的比较
- 二、Set
- 1、HashSet底层机制
一、List 1、ArrayList底层操作机制
(1)ArrayList中维护了一个Object类型数组——就是说可以存储不同类型的对象
(2)当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需再次扩容,则扩容elementData为1.5倍——本身加右移一位
(3)如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
(4)内部还维护了一个modConter++来记录集合被修改的次数
(5)创建完数组后用Arrays.copyOf()转移数据
(6)ArrayList可以加入多个null
(1)Vector类的定义说明
public class Vectorextends AbstractList implements List , RandomAccess,Cloneable,Serializable
Serializable可序列化网络传输
(2)Vector底层也是一个数组对象,protected Objec[] elementData;
(3)Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArraylndexOutOfBoundsException(index);
return elementData(index);
}
(4)ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高)。在多线程情况下,不建议使用ArrayList;在开发中,需要考虑线程同步安全时,考虑使用Vector
(5)扩容源码
- 下面这个方法就添加数据到 vector 集合
//下面这个方法就添加数据到 vector 集合
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
- 确定是否需要扩容 条件 : minCapacity - elementData.length>0
//确定是否需要扩容 条件 : minCapacity - elementData.length>0
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
- 如果 需要的数组大小 不够用,就扩容 , 扩容的算法,扩容两倍
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容两倍
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
3、Vector 和 ArrayList 的比较
4、linkList底层机制
(1)linkList底层实现了双向链表和双端队列的特点
(2)可以添加任意元素,可以重复,包括null
(3)线程不安全,没有实现同步
(4)linkList底层维护了一个双向循环链表
(5)linkList中维护了两个属性first和last分别指向首节点和尾节点
(6)每个节点(Node对象),里面又维护了prev,next,item三个属性
(1)HashSet实际上是HashMap,看源码,下图
(2)可以使用迭代器,增强for循环,不能使用索引方式
(3)元素无序,不允许重复
(4)可以存放null,只能一个
(5)HashSet不保证元素是有序的,取决于Hash后,在确定索引的结果
(6)源码分析debug
HashCode是Object传进来的,会得到一个哈希值,无符号右移十六位,防止冲突
这个是负载因子,负载因子*容量=临界值,为什么不是16在扩容?而是12因为一旦有大量add操作会卡住会阻塞,12相当于缓冲,至于16 ,0.75负载因子,8的阈值,6的阈值是怎么确定的?还是因为哈希冲突归根结底大多数问题最终都会回归到数学里的极限和概率论,并且这个12是只要往里添加节点,就会+1,往链子上+也算,并不是数组的大小,,这里不展开,改天在另开一篇细说。
这个afternode是留给hashmap的子类来实现的,对于hashmap是一个空方法,源码里也能看出来留给linkhashmap双向链表。
equals方法的标准是程序员自己决定的,不能简单地认为是比较内容
hashset——new hashmap——hashset.add方法——add里面会用到map.put——debug时候会看到hashmap的put方法——put里的key是传入的,value是在调用add方法时的那个Present,Present是hashset的静态的对象,是共享的,这个对象没什么意义,是起到站位的目的——所以value是不变的,key和value这两个参数说清楚了——现在看put内的方法putVal——可以看到putVal调用了hash()方法——hash方法内部的算法——调用了Object的hashcode在^(h>>>16)减少哈希碰撞,得到哈希值,——而hashcode是native方法,表明这是本地方法由C++实现,供java调用,到了C++里面即使具体的算法代码实现——现在putVal的参数都清楚了,下面就是看putVal的具体实现——putVal调用了resize()方法进行扩容
整理图花好久,等写完另外一篇,在写这篇



