集合源码探索

一、迭代器(Iterator)

  JAVA集合以面向对象的思想分为两大类,单例集合与双例集合。
  Collection为单例集合,Map为双例集合

  Collection接口是继承自Iterable接口,Iterable接口是一个迭代器

   它有iterator()这个⽅法,返回的是Iterator 再来看⼀下,Iterator也是⼀个接⼝,它只有三个⽅法:

  • hasNext()
  • next()
  • remove()

  可是,我们没能找到对应的实现⽅法,只能往Collection的⼦类下找找了,于是我们找到了--- >ArrayList于是,我们在ArrayList下找到了iterator实现的身影:

  它是在ArrayList以内部类的⽅式实现的!并且, 从源码可知:Iterator实际上就是在遍历集合

二、Collection接口

  Collection接口继承自Iterable接口
  Collection继承体系

  Collection功能:

三、List集合

  Collection主要学习集合的类型两种:Set和List
  List集合的特点就是:有序(存储顺序和取出顺序⼀致),可重复

  Collection返回的是Iterator迭代器接⼝,⽽List中⼜有它⾃⼰对应的实现-->ListIterator接⼝ 该接⼝⽐普通的Iterator接⼝多了⼏个⽅法

  从⽅法名就可以知道:ListIterator可以往前遍历,添加元素,设置元素
  List集合常⽤的⼦类有三个:
  • ArrayList 底层数据结构是数组。线程不安全
  • LinkedList 底层数据结构是链表。线程不安全
  • Vector 底层数据结构是数组。线程安全

四、Set集合

  从Set集合的⽅法我们可以看到:⽅法没有⽐Collection要多
  Set集合的特点是:元素不可重复

  Set集合常⽤⼦类
  • HashSet集合 :
  底层数据结构是哈希表(是⼀个元素为链表的数组)
  • TreeSet集合:
  底层数据结构是红⿊树(是⼀个⾃平衡的⼆叉树)
  保证元素的排序⽅式
  • LinkedHashSet集合:
  底层数据结构由哈希表和链表组成。
 

五、ArrayList解析

  ArrayList继承自AbstractList,实现了List、Cloneable、Serializable、RandomAccess接口

  ArrayList属性:

  根据上⾯我们可以清晰的发现:ArrayList底层其实就是⼀个数组,ArrayList中有扩容这么⼀个概念, 正因为它扩容,所以它能够实现“动态”增⻓

5.1、构造方法

5.2、Add方法

5.2.1 add(E e)

  将指定的元素追加到此列表的末尾。
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
 
步骤:
  • 检查是否需要扩容 插⼊元素,确认list容量,尝试容量加1,看看有⽆必要
  • 插入元素

  随后调⽤ ensureExplicitCapacity() 来确定明确的容量,我们也来看看这个⽅法是怎么实现的:

  所以,接下来看看 grow() 是怎么实现的

  进去看 copyOf() ⽅法

  到⽬前为⽌,我们就可以知道 add(E e) 的基本实现了:
  ⾸先去检查⼀下数组的容量是否⾜够
  • ⾜够:直接添加
  • 不⾜够:扩容
  • 扩容到原来的1.5倍
  • 第⼀次扩容后,如果容量还是⼩于minCapacity,就将容量扩充为minCapacity。

5.2.2 add(int index, E element)

  步骤实现:
  检查⻆标
  空间检查,
  如果有需要进⾏扩容 插⼊元素
  我们发现,与扩容相关ArrayList的add⽅法底层其实都是 arraycopy() 来实现的 看到 arraycopy() ,我们可以发现:该⽅法是由C/C++来编写的,并不是由Java实现:

  总的来说: arraycopy() 还是⽐较可靠⾼效的⼀个⽅法。

5.3、get方法

// 检查⻆标
private void rangeCheck(int index) {
 if (index >= size)
 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 返回元素
E elementData(int index) {
 return (E) elementData[index];
}
  实现步骤:
  • 检查角标
  • 返回元素

5.4、set方法

//检查角标
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

//替换元素
E elementData(int index) {
    return (E) elementData[index];
}

//返回旧值
return oldValue;
  实现步骤:
  • 检查⻆标
  • 替代元素
  • 返回旧值

5.5、remove方法

实现步骤:
  • 检查⻆标
  • 删除元素
  • 计算出需要移动的个数,并移动
  • 设置为null,让Gc回收
  ArrayList是基于动态数组实现的,在增删时候,需要数组的拷⻉复制。 ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的⼀半,也就是变为原来的1.5倍

5.6、Vector与ArrayList区别

  Vector是jdk1.2的类了,⽐较⽼旧的⼀个集合类。

  Vector底层也是数组,与ArrayList最⼤的区别就是:同步(线程安全) Vector是同步的
  我们可以从⽅法上就可以看得出来:

  在要求⾮同步的情况下,我们⼀般都是使⽤ArrayList来替代Vector的了~ 如果想要ArrayList实现同步,可以使⽤Collections的⽅法:就可以实现同步了
List list = Collections.synchronizedList(new ArrayList(...));
还有另⼀个区别:
  • ArrayList在底层数组不够⽤时在原来的基础上扩展0.5倍,Vector是扩展1倍。

  虽然同步容器的所有方法都加了锁,但是对这些容器的复合操作无法保证其线程安全性。需要客户端通过主动加锁来保证。
public Object deleteLast(Vector v){
    int lastIndex  = v.size()-1;
    v.remove(lastIndex);
}
  上面这个方法是一个复合方法,包括size()和remove(),乍一看上去好像并没有什么问题,无论是size()方法还是remove()方法都是线程安全的,那么整个deleteLast方法应该也是线程安全的。
  但是时,如果多线程调用该方法的过程中,remove方法有可能抛出ArrayIndexOutOfBoundsException。
  我们上面贴了remove的源码,我们可以分析得出:当index >= elementCount时,会抛出ArrayIndexOutOfBoundsException ,也就是说,当当前索引值不再有效的时候,将会抛出这个异常。
  因为removeLast方法,有可能被多个线程同时执行,当线程2通过index()获得索引值为10,在尝试通过remove()删除该索引位置的元素之前,线程1把该索引位置的值删除掉了,这时线程一在执行时便会抛出异常。

  为了避免出现类似问题,可以尝试加锁:
public void deleteLast() {
    synchronized (v) {
        int index = v.size() - 1;
        v.remove(index);
    }
}

六、LinkedList解析

  LinkedList底层是双向链表

  从结构上,我们还看到了LinkedList实现了Deque接⼝,因此,我们可以操作LinkedList像操作队列 和栈⼀样

6.1、构造方法

6.2、add方法

6.3、remove方法

6.4、get方法

6.5、set方法

  set⽅法和get⽅法其实差不多,根据下标来判断是从头遍历还是从尾遍历
public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}
 

6.6、List集合总结

  • ArrayList:

  底层实现是数组 ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的⼀半,也就是变为原来的1.5倍 在增删时候,需要数组的拷⻉复制(navite ⽅法由C/C++实现)
  • LinkedList:

  底层实现是双向链表[双向链表⽅便实现往前遍历]
  • Vector:

  底层是数组,现在已少⽤,被ArrayList替代
  原因有两个: Vector所有⽅法都是同步,有性能损失。
  Vector初始length是10 超过length时 以100%⽐率增⻓,相⽐于ArrayList更多消耗内存。
  总的来说:查询多⽤ArrayList,增删多⽤LinkedList。
  ArrayList增删慢不是绝对的(在数量⼤的情况下,已测试): 如果增加元素⼀直是使⽤ add() (增加到末尾)的话,那是ArrayList要快 ⼀直删除末尾的元素也是ArrayList要快【不⽤复制移动位置】 ⾄于如果删除的是中间的位置的话,还是ArrayList要快!
但⼀般来说:增删多还是⽤LinkedList,因为上⾯的情况是极端的

七、Set解析

  Set特性:
  • Set<E> extends Collection<E>
  • Set接口中的元素是无序不可重复的
  • Set集合的底层就是Map

7.1 HashSet

  HashSet特性:
  • HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
  • 实现Set接⼝
  • 不保证迭代顺序
  • 允许元素为null
  • 底层实际上是⼀个HashMap实例
  • ⾮同步
  • 初始容量⾮常影响迭代性能,不要讲初始容量设置太高(或负载因数过低)
  我们知道Map是⼀个映射,有key有value,既然HashSet底层⽤的是HashMap,那么value在哪⾥呢?
value是⼀个Object,所有的value都是它 所以可以直接总结出:HashSet实际上就是封装了HashMap,操作HashSet元素实际上就是操作 HashMap。这也是⾯向对象的⼀种体现,重⽤性贼⾼!

7.2 TreeSet

  TreeSet特性:
  • TreeSet<E> extends AbstractSet<E>implements NavigableSet<E>, Cloneable, java.io.Serializable
  • 实现NavigableSet接⼝
  • 可以实现排序功能
  • 底层实际上是⼀个TreeMap实例
  • ⾮同步

7.3 LinkedHashSet

 

  LinkedHashSet特性:
  • LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable
  • 迭代是有序的 允许为null
  • 底层实际上是⼀个HashMap+双向链表实例(其实就是LinkedHashMap)...
  • ⾮同步 性能⽐HashSet差⼀丢丢,因为要维护⼀个双向链表
  • 初始容量与迭代⽆关,LinkedHashSet迭代的是双向链表

7.4 Set集合总结

  可以很明显地看到,Set集合的底层就是Map

  HashSet:

  • ⽆序,允许为null,底层是HashMap(散列表+红⿊树),⾮线程同步

  TreeSet:

  • 有序,不允许为null,底层是TreeMap(红⿊树),⾮线程同步
  LinkedHashSet:
  • 迭代有序,允许为null,底层是HashMap+双向链表,⾮线程同步

八、 Map集合解析

  Map在《Core Java》中称之为-->映射
  映射模型:

  Map与Collection的区别:

8.1 HashMap 解析

 我们知道Hash的底层是散列表,⽽在Java中散列表的实现是通过数组+链表的~ 再来简单看看put⽅法就可以印证我们的说法了:数组+链表-->散列表
 

  HashMap特性:
  • ⽆序,允许为null,⾮同步
  • 底层由散列表(哈希表)实现
  • 初始容量和装载因⼦对HashMap影响挺⼤的,设置⼩了不好,设置⼤了也不好、

8.2 构造方法

  HashMap的构造⽅法有4个:
  在上⾯的构造⽅法最后⼀⾏,我们会发现调⽤了 tableSizeFor() ,我们进去看看: 看完上⾯可能会感到奇怪的是:为啥是将2的整数幂的数赋给threshold?
  threshold这个成员变量是阈值,决定了是否要将散列表再散列。
  它的值应该是: capacity * load factor 才对的。
  其实这⾥仅仅是⼀个初始化,当创建哈希表的时候,它会重新赋值的:
posted @ 2023-05-23 17:27  IM-Fly  阅读(17)  评论(0)    收藏  举报