Java基础--集合分析

简介

Java 集合, 也叫作容器,主要是由两大接口派生而来

  • Collection 接口,主要用于存放单一元素
  • Map 接口,主要用于存放键值对

图中的绿色的虚线代表实现,绿色实线代表接口之间的继承,蓝色实线代表类之间的继承


Collection

List

在Collection中,List集合是有序的,可以通过索引来访问元素(LinkedList除外),遍历元素

在 List 接口下有 ArrayList、LinkedList、Vector 实现类。

  • ArrayList 底层通过 Object数组 实现,随着元素的增加而动态扩容
  • LinkedList 底层通过 双向链表 来实现,随着元素的增加不断向链表的后端增加节点
  • Vector 底层通过 Object数组 实现,随着元素的增加而动态扩容,并且是线程安全的

ArrayList

特点
  • ArrayList底层通过Object数组实现,容量不固定,随着容量的增加而动态扩容,数据有序
  • 支持插入元素为null
  • 不支持多线程操作,即线程不安全

它继承AbstractList,实现List、RandomAccess、Cloneable、Serializable接口

  1. 实现 List,得到了 List 集合框架基础功能,如添加、删除元素功能
  2. 实现 RandomAccess,获得了快速随机访问存储元素的功能,该接口是一个标记接口,没有任何方法
  3. 实现 Cloneable,得到了 clone() 方法,可以实现克隆功能
  4. 实现 Serializable,表示可以被序列化,通过序列化去传输
源码分析
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接口

  1. LinkedList实现 List,得到了 List 集合框架基础功能,如添加、删除元素功能
  2. LinkedList实现 Deque,Deque 是一个双向队列,也就是既可以先入先出,又可以先入后出
  3. LinkedList实现 Cloneable,得到了 clone() 方法,可以实现克隆功能
  4. LinkedList实现 Serializable,表示可以被序列化,通过序列化去传输
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等接口

  1. Vector 实现 List,得到了 List 集合框架基础功能,如添加、删除元素功能
  2. Vector 实现 Cloneable,得到了 clone() 方法,可以实现克隆功能
  3. 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

Set 继承于 Collection接口,是一个不允许出现重复元素,并且元素无序的集合,主要有 HashSet 和 TreeSet两大实现类

  • HashSet 是哈希表结构,主要利用 HashMap 的key来存储元素,计算插入元素的hashCode来获取元素在集合中的位置
  • TreeSet 是红黑树结构,每个元素都是树中的一个结点,插入的元素都会进行排序

HashSet

特点

HashSet 实现 Set接口,底层由HashMap来实现,为哈希表结构,新增元素相当于 HashMap 的 key,value 默认为一个固定的 Object

它继承于 AbstractSet,实现了Set, Cloneable, Serializable接口

  1. HashSet 继承 AbstractSet类,获得了 Set 接口大部分的实现,减少了实现此接口所需的工作,实际上是又继承了 AbstractCollection 类
  2. HashSet 实现了 Set接口,获取 Set 接口的方法,可以自定义具体实现,也可以继承 AbstractSet 类中的实现
  3. HashSet 实现 Cloneable,得到了 clone() 方法,可以实现克隆功能
  4. HashSet 实现 Serializable,表示可以被序列化,通过序列化去传输

特点:不允许出现重复数据,允许插入null值,元素无序,线程不安全

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 的主体,链表则是主要为了解决哈希冲突而存在的

posted @ 2022-07-24 16:41  伊文小哥  阅读(29)  评论(0编辑  收藏  举报