1. List接口和常用方法

1.1 List接口基本介绍

1、返回集合长度
int size()
2、添加元素
void add(Object ele), void add(int index, Object ele)
3、添加多个元素
boolean addAll(int index, Collection eles)
4、获取指定index位置的元素
Object get(int index)
5、返回obj在集合中首次出现的位置
int indexOf(Object obj)
6、返回obj在集合中末次出现的位置
int lastIndexOf(Object obj)
7、移除指定index位置的元素,并返回此元素
Object remove(int index)
8、设置指定index位置的元素为ele,相当于替换,索引必须存在
Object set(int index, Object ele)
9、返回从fromIndex到toIndex位置的子集合,前闭后开
List subList(int fromIndex, int toIndex)

1.2 List接口的三种遍历方式

img

2. ArrayList

2.1 注意事项

img

2.2 ArrayList的底层操作机制源码分析(重点)

img

使用无参构造器

img

  • 可以看出elementData一开始是个空数组
    img
    img

  • 执行list.add(i)时,会先把int类型的i转化为Integer类型
    img

  • 然后执行add方法,首先确认数组大小够不够,然后再对数组元素赋值
    img

  • 如何确认数组大小够不够呢

    • 当数组为空时
      • 先设置数组需要的最小长度minCapacity为10
      • 然后判断数组需要的最小长度是否大于数组长度,此时一定大于,因为数组长度为0
      • 大于则扩容
    • 当数组不为空时
      • 先计算数组所需要的最小长度,即索引的位置+1,minCapacity=size+1
      • 然后计算当前数组的长度elementData.length
      • 判断数组所需的最小长度是否大于当前数组长度,大于则扩容(即当索引+1超出数组长度时,扩容)
    • 其中modCount用来记录集合被修改的次数
      img
      img
  • 如何扩容呢-调用grow函数

    • 当第一次扩容,即数组为空时,elementData.length=0
      • 通过位运算计算得到的newCapacity还是0,所以需要加个判断条件,给newCapacity赋值为minCapacity
      • 然后使用Arrays的copyOf方法进行数组扩容
    • 当不是第一次扩容时
      • 根据位运算在原来数组长度上扩大1.5倍得到,向下取整,newCapacity
      • 然后使用Arrays的copyOf方法进行数组扩容

img

使用有参构造器

img

  • 首先创建一个指定大小elementData数组
    img

  • 其它流程在无参构造器中以出现,不再重复

总结

  • 关键是维护一个elementData数组,当new一个对象时,
    • 使用无参构造时,它为空数组
    • 使用有参构造时,它的长度为传入的参数值,并且给elementData new一个长度为参数大小的数组
  • 当使用add方法时,首先会计算容器所需要的最小容量是多少,即minCapacity
    • 如果是空数组,设置最小容量为10
    • 如果不是,最小容量就是索引位置+1(size+1)
  • 然后判断这个最小容量是否大于数组长度
    • 如果大于,就说明要扩容了
    • 如果小于,什么也不干
  • 扩容时,扩容大小使用位操作计算,当前容量+右移一位,(右移一位会小一半),所以扩大1.5倍
    • 但是当容量为0时,0右移一位还是0,所以加了一个判断,给这个容量直接赋值为10

关键是维护一个elementData数组,如果是无参构造,一开始将elementData数组初始化为空数组,当执行添加操作时,需要第一次扩容到10,后面按1.5倍扩容;如果是有参构造,一开始将elementData的大小初始化为参数值,之后每次扩容按1.5倍扩,扩容是创建一个新数组,然后调用一个本地方法将原来数组的值复制到新数组

3. Vector

3.1 基本介绍

img

3.2 Vector与ArrayList的比较

img

3.3 源码分析

img

  • 首先通过构造方法设置数组初始容量,并创建一个指定长度的数组

    • 无参构造器默认设置初始容量为10
      • img
    • 有参构造器根据指定数值设置容量,扩容增量默认为0
      • img
    • 还可以自己设置扩容增量
      • img
  • 然后对int类型自动装箱

    • img
  • 接着执行add方法

    • 首先确认当前的容量大小是否需要扩容
      img
      img

    • 如果需要扩容,且设置了扩容增量capacityIncrement的话,就扩容这个值的大小,如果没设置扩容增量,就扩容原来的两倍
      img

  • 最后将数据添加到Vector集合中

3.4 总结

  • 如果是无参构造,直接默认创建一个初始容量为10的数组,之后每次需要扩容时,扩大两倍
  • 如果是有参构造,创建初始容量为指定值的数组,如果设置了扩容增量,之后每次扩容时,扩容后的大小等于当前容量+扩容增量,如果没设置扩容增量,则扩容两倍。

4. LinkedList

基本介绍

img
img

源码分析

add()函数

img

  • 首先通过无参构造方法初始化属性
    img
    img

  • 对int类型自动装箱后,执行add方法
    img

  • 其中最关键的方法是linkLast方法

    • 当linkList中没有元素时
      • 首先创建临时节点,使用l记录last的位置
      • 然后创建新节点,让它的pre指向临时节点、next指向null
      • 更新双指针的位置,将last和first都指向该新节点
    • 当linkList中有元素时
      • 首先创建临时节点,使用l记录last的位置
      • 然后创建新节点,让它的pre指向临时节点、next指向null
      • 然后让临时节点的next指针指向新节点
      • 大概思路是更新新节点和原来最后一个节点之间的pre和next指针指向
        img

remove()函数

  • 删除节点默认删除第一个节点
    img
    img
  1. 首先它直接调用了removeFirst()方法
    img
    img

  2. 真正删除第一个节点的方法是unLinkFirst()
    img

  • 首先使用临时变量保存原来first指向的节点 的元素值和next指针
  • 清空原来first指向的节点 的元素值和next指针
  • 改变first指针指向临时变量保存的next指针(即原来第二个节点)
    • 但next可能为null(即原list只有一个节点的情况)
      • 此时将last指针也指向null
    • 当next不为null
      • 将next指针(即新的第一个节点)的prev指向null
  • 此时已实现first指向原第二个节点,如果第二个节点为null,first和last都指向null
  • 最后返回删除的节点的元素值

总结:

  • 新增节点:先用临时变量记录最后一个节点,然后创建一个新节点,将新节点连接到原来链表上(新节点的pre指针指向临时节点,next指针指向null,最后临时节点的next指针指向新节点),更新last的位置,如果原来为空,还要将first也指向这个节点
  • 删除节点:先用临时节点保存第一个节点,然后清空第一个节点next指针和元素值,将firs指向第二个节点,如果这个节点为空,将last也指向它;
  • 总结:先用临时变量保存需要处理的节点,然后再处理该节点,特殊情况是只有一个节点或没有节点的时候,此时两个指针都要移动。

5. List集合选择

img

  • ArrayList可以通过索引直接定位
  • linkedlist通过遍历查找
    注意:线程都是不安全的
    补充:
    Alt+7:查看当前类的所有方法

Set接口


  • 常用方法和遍历方式

  • 存放顺序和取出顺序不一致,但是取出顺序是固定的

  • set接口对象不能通过索引获取,它没有get方法

HashSet


  • 注意:是当一条链表的个数达到8个,同时table元素达到64个,才会树化
  • 当一条链表达到8个了,但是元素少数64个,table会扩容

  • 扩容时,不是数组里的元素达到阈值才扩容,而是所有元素数量(包括链表上的)达到阈值就会扩容

HashSet的底层就是HashMap,只是在HashSet里,value是存放的一个常量对象,平时的操作只用key
当添加元素时,

Map接口

Node实现了Entry接口,Entry接口里有getKey和getValue方法,然后EntrySet集合里存放着Node类型的对象(多态)

遍历方式

  • 第一组:先取出所有的key,通过key取出对应的value
  • 第二组:把所有的value取出
  • 第三组:通过EntrySet 来获取 k-v

小结

HashMap底层

HashMap扩容机制

源码

  1. 执行构造器 new HashMap()
  2. 执行put,调用hash方法
  3. 执行putVal
  4. 关于转成红黑树

HashTable





集合选择

posted on 2023-10-21 23:05  Cheyaoyao  阅读(3)  评论(0编辑  收藏  举报