Java基础--集合分析
简介
Collection
List
List集合是有序的
在 List 接口下有 ArrayList、LinkedList、Vector
实现类。
- ArrayList 底层通过 Object数组 实现,随着元素的增加而动态扩容
- LinkedList 底层通过 双向链表 来实现,随着元素的增加不断向链表的后端增加节点
- Vector 底层通过 Object数组 实现,随着元素的增加而动态扩容,并且是
ArrayList
特点
- 动态扩容,数据有序
- 支持插入元素为null
- 不支持多线程操作,即线程不安全
- 实现 RandomAccess,获得了快速随机访问存储元素的功能,该接口是一个标记接口,没有任何方法
- 实现 Cloneable,得到了 clone() 方法,可以实现克隆功能
源码分析
ArrayList 初始化源码分析
ArrayList
根据 ArrayList 源码 ,-
//创建一个空的ArrayList List<String> list = new ArrayList<>();
//初始 ArrayList 过程--源码 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transient Object[] elementData; private int size; public ArrayList() { //让 elementData数组 指向默认容量大小的 Object数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA,默认容量为0), 其它成员变量中 size 初始化默认值为 0 //在执行add(E e)操作的时候才会开辟 Object数组容量 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
-
- 向 list 中添加元素。查看 ArrayList 的初始化 Object数组容量 的过程
-
//向 list 中添加元素 list.add("a");
查看代码
//向List中添加元素过程--源码 public boolean add(E e) { //确保 Object数组容量 足够 ensureCapacityInternal(size + 1); elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { //计算 Object数组 添加一个元素所需要的的最小容量是多少 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity(Object[] elementData, int minCapacity) { //如果 Object数组 开始时为空,则初始化长度为10 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { modCount++; //判断 Object数组 是否有容量存储数据。即所需的最小容量- Object数组原来的长度是否大于0,大于0,则表示 Object数组 容量不足,需要扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { //获取原数组长度 int oldCapacity = elementData.length; //Object数组扩容为原来的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) //如果扩容后的容量大于所需的容量,则只取所需容量的大小即可 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //把原数组的内容复制到更大容量的数组里面 elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
-
ArrayList
-
//创建一个指定容量的ArrayList List<String> list2 = new ArrayList<>(22);
//指定初始化容量的源码 public ArrayList(int initialCapacity) { if (initialCapacity > 0) { //如果指定的初始化容量大于0,则初始化指定的容量 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { //如果指定的初始化容量等于0,则初始化为一个空数组 this.elementData = EMPTY_ELEMENTDATA; } else { //初始化容量值小于0,参数错误 throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
-
- 移除 ArrayList 中的元素
-
//移除ArrayList中元素 list.remove("123");
public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { //移除第一个元素值为 null 的 fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { //移除第一个元素值为 o 的 fastRemove(index); return true; } } return false; } private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) //后面的元素往前移动 System.arraycopy(elementData, index+1, elementData, index, numMoved); //将尾部元素的值置空,让GC回收内存 elementData[--size] = null; }
-
ArrayList 扩容机制源码分析
- 当不断的向ArrayList添加元素时,数组容量不足以存储后来的元素,则需要扩容:为原来长度的1.5倍
-
private void ensureExplicitCapacity(int minCapacity) { modCount++; //判断 Object数组 是否有容量存储数据。即存储元素所需的最小容量- Object数组原来的长度是否大于0,大于0,则表示 Object数组 容量不足,需要扩容 if (minCapacity - elementData.length > 0) //根据当前存储元素所需的最小容量进行扩容 grow(minCapacity); } //ArrayList扩容机制,源码如下 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { //获取原数组长度 int oldCapacity = elementData.length; //Object数组扩容为原来的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) //如果扩容后的容量大于所需的容量,则只取所需容量的大小即可 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //把原数组的内容复制到更大容量的数组里面 elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
-
LinkedList
特点
- LinkedList是一个双向链表,每一个节点都拥有指向前后节点的引用和数据域。相比于ArrayList来说,其占用的存储空间更多,因为多了两个保存前后结点的指针域
- 相比于ArrayList来说,LinkedList的随机访问效率更低,因为需要从头或者尾开始扫描查找数组
- 线程不安全,不支持多线程操作
它继承AbstractSequentialList,实现了List, Deque, Cloneable, Serializable接口
- LinkedList实现 List,得到了 List 集合框架基础功能
- LinkedList实现 Deque,Deque 是一个双向队列,也就是既可以先入先出,又可以先入后出
- LinkedList实现 Cloneable,得到了 clone() 方法,可以实现克隆功能
LinkedList 初始化源码分析
- 初始化一个 LinkedList
-
//初始化一个 LinkedList List<String> linkedList = new LinkedList<>();
//初始化源码分析 //链表结点个数 transient int size = 0; //指向第一个结点的指针 (first == null && last == null) || (first.prev == null && first.item != null) transient Node<E> first; //指向最后一个结点的指针 (first == null && last == null) || (last.next == null && last.item != null) transient Node<E> last; //初始化 LinkedList,头结点和尾结点初始化为null,链表结点个数初始化为 0 public LinkedList() { } //节点类如下 private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
-
- 向 LinkedList 中添加元素
-
//先linkedList添加元素 linkList.add("123");
//向LinkedList的尾部添加元素源码分析 public boolean add(E e) { linkLast(e); return true; } void linkLast(E e) { //获取尾结点 final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); //将插入的结点作为尾结点 last = newNode; //如果原尾结点为 null,即当前链表为空,则将first结点指向新节点,否则原尾结点的下一个结点为新节点 if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
-
- 移除 LinkedList 中元素
-
//移除LinkedList中元素 linkList.remove("123");
//移除LinkedList中元素源码分析 public boolean remove(Object o) { if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { //移除第一个结点值为 null unlink(x); return true; } } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { //移除第一个结点值为 o unlink(x); return true; } } } return false; } E unlink(Node<E> x) { //将该节点从链表中断开 final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
-
Vector
特点
- Vector 是基于 Object数组 来实现的,容量能够动态增长
- 它的很多实现方法都加入了同步语句(synchronized),因此是线程安全的
Vector继承于AbstractList,实现了List、RandomAccess、Cloneable、 Serializable等接口
- Vector 实现 List,得到了 List 集合框架基础功能
- Vector 实现 Cloneable,得到了 clone() 方法,可以实现克隆功能
- Vector 实现 Serializable,表示可以被序列化,通过序列化去传输
Vector 初始化源码分析
- 初始化一个 Vector
-
//初始化一个 Vector List<String> vector = new Vector<>();
//初始化源码分析 //存储元素的数组 protected Object[] elementData; //数组扩容时指定增长的步长 protected int capacityIncrement; //使用空参构造Vector时,初始化数组的默认长度为 10 public Vector() { this(10); } public Vector(int initialCapacity) { //初始化数组扩容时指定增长的步长默认为 0 this(initialCapacity, 0); } public Vector(int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; this.capacityIncrement = capacityIncrement; }
-
- 向 Vector 中添加元素
-
//向Vector中添加元素 vector.add("123");
//向Vecotr中添加元素的源码分析 //add(E e) 方法被 synchronized 修饰,则该方法是线程安全的 public synchronized boolean add(E e) { modCount++; //计算数组的容量是否足够保存新增元素 ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; } private void ensureCapacityHelper(int minCapacity) { // 如果保存元素所需的容量大于原数组的长度,则需要扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { //获取原数组的长度 int oldCapacity = elementData.length; //计算保存新元素所需的容量。如果数组扩容的步长为 0,则数组扩容为原来的两倍,否则按照指定的步长扩容数组 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); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
-
- 移除 Vector 中元素
-
//移除Vector中元素 vector.remove("123");
查看代码
//移除Vector中的值源码分析 public boolean remove(Object o) { return removeElement(o); } //removeElement(Object obj) 方法被 synchronized 修饰,所以该方法是线程安全的 public synchronized boolean removeElement(Object obj) { modCount++; //找到 obj 元素在数组中的下标值 int i = indexOf(obj); if (i >= 0) { //根据下标值移除元素 removeElementAt(i); return true; } return false; } public int indexOf(Object o) { //从下标值为0处开始查找 o return indexOf(o, 0); } //indexOf(Object o, int index) 方法被 synchronized 修饰,所以该方法是线程安全的 public synchronized int indexOf(Object o, int index) { if (o == null) { for (int i = index ; i < elementCount ; i++) if (elementData[i]==null) return i; } else { for (int i = index ; i < elementCount ; i++) if (o.equals(elementData[i])) return i; } return -1; } //removeElementAt(int index) 方法被 synchronized 修饰,所以该方法是线程安全的 public synchronized void removeElementAt(int index) { modCount++; if (index >= elementCount) { throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); } else if (index < 0) { throw new ArrayIndexOutOfBoundsException(index); } int j = elementCount - index - 1; if (j > 0) { System.arraycopy(elementData, index + 1, elementData, index, j); } elementCount--; elementData[elementCount] = null; /* to let gc do its work */ }
-
Vector 扩容机制源码分析
- 如果数组扩容的步长为 0,则数组扩容为原来的两倍,否则按照指定的步长扩容数组
-
//Vecotr中扩容的源码分析 private void ensureCapacityHelper(int minCapacity) { // 如果保存元素所需的容量大于原数组的长度,则需要扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { //获取原数组的长度 int oldCapacity = elementData.length; //计算保存新元素所需的容量。如果数组扩容的步长为 0,则数组扩容为原来的两倍,否则按照指定的步长扩容数组 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); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
-
Set
不允许出现重复元素,并且元素无序的集合,主要有 HashSet 和 TreeSet两大实现类
- HashSet 是哈希表结构,主要利用 HashMap 的key来存储元素,计算插入元素的hashCode来获取元素在集合中的位置
- TreeSet 是红黑树结构,每个元素都是树中的一个结点,插入的元素都会进行排序
HashSet
特点
HashSet 实现 Set接口,底层由HashMap来实现,为哈希表结构,新增元素相当于 HashMap 的 key,value 默认为一个固定的 Object
它继承于 AbstractSet,实现了Set, Cloneable, Serializable接口
- HashSet 继承 AbstractSet类,获得了 Set 接口大部分的实现,减少了实现此接口所需的工作,实际上是又继承了 AbstractCollection 类
- HashSet 实现了 Set接口,获取 Set 接口的方法,可以自定义具体实现,也可以继承 AbstractSet 类中的实现
- HashSet 实现 Cloneable,得到了 clone() 方法,可以实现克隆功能
- HashSet 实现 Serializable,表示可以被序列化,通过序列化去传输
特点:
HashSet初始化源码分析
HashSet
-
//创建一个空的HashSet Set<String> set = new HashSet<>();
//初始 HashSet 源码分析 private transient HashMap<E,Object> map; private static final Object PRESENT = new Object(); public HashSet() { //新建一个HashMap map = new HashMap<>(); } ----------------------HashMap源码如下-------------------------------- static final float DEFAULT_LOAD_FACTOR = 0.75f; final float loadFactor; public HashMap() { //初始化加载因子为 0.75f this.loadFactor = DEFAULT_LOAD_FACTOR; }
-
- 向 set 中添加元素
-
//向 set 中添加元素。查看 HashSet 的添加数据的过程 set.add("1");
查看代码
private static final Object PRESENT = new Object(); private transient HashMap<E,Object> map; public boolean add(E e) { //调用HashMap的put方法,PRESENT是一个至始至终都相同的虚值 return map.put(e, PRESENT)==null; } ----------------------------HashMap的源码---------------------------- static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 static final int MAXIMUM_CAPACITY = 1 << 30; public V put(K key, V value) { //先计算插入key的hash值 return putVal(hash(key), key, value, false, true); } static final int hash(Object key) { int h; //获取key的hashCode,如果key为null,则返回 0,否则与该key的hashCode无符号右移16位进行与运算 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } //获取key的hashCode值 public native int hashCode(); //向HashMap中插入key-value transient Node<K,V>[] table; final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; //tab为空则创建,table未初始化或者长度为0,进行扩容 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; //计算index,并对null做处理 //(n - 1) & hash 确定元素存放在哪个桶中,桶为空,生成结点放入桶中(此时该结点是放在数组中) if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); //桶中已经存在元素 else { Node<K,V> e; K k; //节点key存在,直接覆盖value //比较桶中第一个元素(数组中的结点)的hash值相等,key相等 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) // 将第一个元素赋值给e,用e来记录 e = p; else if (p instanceof TreeNode) //判断该链为红黑树 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { //该链为链表.在链表最末插入结点 for (int binCount = 0; ; ++binCount) { //判断该链表尾部指针是不是空的 if ((e = p.next) == null) { //在尾部插入新结点 p.next = newNode(hash, key, value, null); //判断链表的长度是否达到转化红黑树的临界值,临界值为8 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st //链表结构转树形结构 treeifyBin(tab, hash); break; } //判断链表中结点的key值与插入的元素的key值是否相等 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; //用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表 p = e; } } //判断当前的key已经存在的情况下,再来一个相同的hash值、key值时,返回新来的value这个值 if (e != null) { // existing mapping for key V oldValue = e.value; //onlyIfAbsent为false或者旧值为null if (!onlyIfAbsent || oldValue == null) //用新值替换旧值 e.value = value; //访问后回调 afterNodeAccess(e); return oldValue; } } ++modCount; //超过最大容量就扩容 if (++size > threshold) resize(); // 插入后回调 afterNodeInsertion(evict); return null; } //扩容 final Node<K,V>[] resize() { //oldTab指向hash桶数组 Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { //如果oldCap不为空的话,就是hash桶数组不为空 if (oldCap >= MAXIMUM_CAPACITY) { //如果大于最大容量了,就赋值为整数最大的阀值 threshold = Integer.MAX_VALUE; return oldTab; } //如果当前hash桶数组的长度在扩容后仍然小于最大容量 并且oldCap大于默认值16 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K,V> e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { // preserve order Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
-
HashSet
-
//创建一个指定容量的的HashSet Set<String> set = new HashSet<>(3);
//初始 HashSet 过程。分析 HashSet 源码 private transient HashMap<E,Object> map; private static final Object PRESENT = new Object(); static final float DEFAULT_LOAD_FACTOR = 0.75f; static final int MAXIMUM_CAPACITY = 1 << 30; final float loadFactor; public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } ---------------------HashMap源码--------------------- public HashMap(int initialCapacity) { //初始化加载因子为 0.75f this(initialCapacity, DEFAULT_LOAD_FACTOR); } public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); } static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
-
Map--后续补充
HashMap
JDK1.8 之前 HashMap
由数组+链表组成的,数组是 HashMap
的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间
Hashtable
数组+链表组成的,数组是 Hashtable
的主体,链表则是主要为了解决哈希冲突而存在的