一、ArrayList实现类

1. 常用方法

 增加:add(int index, E element)删除:remove(int index)  remove(Object o)修改:set(int index, E element)查看:get(int index)判断:常用遍历方式:
     //List集合 遍历://方式1:普通for循环:System.out.println("---------------------");for(int i = 0;i<list.size();i++){System.out.println(list.get(i));}//方式2:增强for循环:System.out.println("---------------------");for(Object obj:list){System.out.println(obj);}//方式3:迭代器:System.out.println("---------------------");Iterator it = list.iterator();while(it.hasNext()){System.out.println(it.next());}

2. JDK1.8(jdk1.8.0_331)源码下(简要)

重要成员变量和构造方法

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{//重要成员变量private static final int DEFAULT_CAPACITY = 10; //数组默认初始容量private static final Object[] EMPTY_ELEMENTDATA = {}; //实例对象的默认数组,是个空数组private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //与EMPTY_ELEMENTDATA类似,主要用于区分在首次添加元素时判断如何进行扩容transient Object[] elementData; //ArrayList中实际存储元素的Object数组private int size; //计算ArrayList数组的元素数量,不是elementData.lengthprivate static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //要分配的数组的最大大小,也就是数组极限长度protected transient int modCount = 0;//给迭代器用的,在调用add和remove方法时修改数值//构造方法//空参构造,例如List list = new ArrayList<>();public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //初始化数组为空数组}//根据传入的initialCapacity,判断初始化数组长度为多少,例如List list1 = new ArrayList<>(100);public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity]; // >0时初始化} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA; //等于0时初始化为空数组} else {throw new IllegalArgumentException("Illegal Capacity: "+ //小于0时,抛出异常,代码健壮性考虑initialCapacity);}}//传入集合Collection下面的实现类,? extends E这个表达式涉及到泛型知识/*List中 <? extends xxx> 定义了List集合的泛型上限,就是定义了一个天花板,输入的类只能从天花板往下找当前类或子类List中<? super xxx> 定义了泛型的下限,就是定义了一个地板,输入的类只能从地板开始往上找当前类或者父类例如:List<Object> a = new ArrayList<>();List<Person> b = new ArrayList<>();List<Student> c = new ArrayList<>();List<? extends Person> list1 = null;List<? super Person> list2 = null;list1 = a;//编译报错,因为list1定义的泛型最大父类(天花板)就是Person,Object类是所有类的父类,超过了定义的天花板Person类list1 = b;//编译正常list1 = c;//编译正常list2 = a;//编译正常list2 = b;//编译正常list2 = c;//编译报错,因为list2定义的泛型最小父类(地板)就是Person,Student类是Person类子类,是低于定义的地板Person类List<? extends Person>:就相当于:List<? extends Person>是List<Person>的父类,也是List<Person的子类>的父类List<? super Person>是List<Person>的父类,也是List<Person的父类>的父类*/public ArrayList(Collection<? extends E> c) {Object[] a = c.toArray(); //转成Obj数组,赋值给aif ((size = a.length) != 0) { //判断数组长度是否为0if (c.getClass() == ArrayList.class) { //判断c的类名是不是ArrayList//是的话就直接赋值给数组elementDataelementData = a; } else {//否则就拷贝复制新数组给elementDataelementData = Arrays.copyOf(a, size, Object[].class); }} else {//数组长度不为0,则实例初始化为空数组elementData = EMPTY_ELEMENTDATA;}}
}

常用方法:add

 //常用方法addpublic boolean add(E e) {ensureCapacityInternal(size + 1);//这一步是计算和判断数组容量elementData[size++] = e; //将传入元素放在数组下标为size的位置,然后size+1,下标是从0开始return true;}private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//如果数组是个空数组的话,返回DEFAULT_CAPACITY和minCapacity的最大值return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;}private void ensureExplicitCapacity(int minCapacity) {modCount++; //迭代器用的计数值,可忽略if (minCapacity - elementData.length > 0) //当 size+1 也就是当前数组元素数量+1,大于当前elementData数组长度时,就需要针对elementData数组扩容grow(minCapacity);}private void grow(int minCapacity) {int oldCapacity = elementData.length; //原先老数组长度int newCapacity = oldCapacity + (oldCapacity >> 1); //新数组长度计算,大概是1.5倍,旧数组长度+(旧数组长度/2)if (newCapacity - minCapacity < 0)newCapacity = minCapacity; //newCapacity < minCapacity时,newCapacity = minCapacityif (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity); //newCapacity超出数组定义的极限长度时的处理elementData = Arrays.copyOf(elementData, newCapacity);}private static int hugeCapacity(int minCapacity) { //超出极限长度时if (minCapacity < 0) // overflowthrow new OutOfMemoryError(); //抛出异常//判断是否大于MAX_ARRAY_SIZE,返回是话Integer.MAX_VALUE,否则MAX_ARRAY_SIZE。//MAX_ARRAY_SIZE之所以是Integer.MAX_VALUE - 8,多出来的8 int(整数) 就等于32 bytes(字节)是防止内存溢出用的,更深入的话涉及JVM和计算机底层了。return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }

样例理解:插入第一个元素时

插入超过10个元素时:

常用方法:remove,这个可以自己看源码,实际原理就是先找到元素e,然后通过System.arraycopy方法把元素e后面所有元素往前移动一位,再把元素末尾置位null

    public E remove(int index) {rangeCheck(index); //下标检查modCount++;E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its workreturn oldValue;}public boolean remove(Object o) {if (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {fastRemove(index);return true;}} else {for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {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);elementData[--size] = null; // clear to let GC do its work}

总结:

  • ArrayList底层是使用Object数组实现的,当创建对象时,若使用无参构造,则初始容量为0,此时第一次调用add方法添加就需要扩容为10,元素数量超过数组长度时会进行数组扩容,如需再次扩容为1.5倍。
  • 优点:底层是数组,查询效率高,数据可重复
  • 缺点:添加或者删除时效率低

3.在JDK1.7和1.8中的区别

最大的区别实际就是new ArrayList()的时候,1.7是直接初始化elementData数组长度为10。而1.8中是初始化elementData数组长度为空数组,在第一次调用add方法时才会初始化elementData数组长度为10。

4.ListIterator迭代器


出错原因:就是迭代器it和list同时对集合进行操作导致。
出错详解:调用add方法时,ArrayList成员变量里操作次数modCount发生了改变,例如上述代码调用到的add(“ee”)时ArrayList中的成员变量modCount=5(执行了5次add)。list.iterator()返回的Iterator iterator实际是ArrayList源码中内部类Itr实现了接口Iterator。在ArrayList源码中内部类Itr有个成员变量expectedModCount(预期的操作次数)记录,在调用完add(“kk”)后modCount=6,但是expectedModCount还是5,然后继续while走到if时调用next()方法时发现两个变量不一致抛出了并发修改异常。
源码图解:

解决方案:事情让一个“人”做 --》引入新的迭代器:ListIterator。迭代和添加操作都是靠ListIterator来完成的,ListIterator底层实际就通过在同一个数组上,用指针的概念在调整位置。

实际上看源码就知道做了什么事情,就是ArrayList源码实现接口ListIterator自己实现了add方法,把expectedModCount和modCount同步一下。同时内部类Itr实现了remove方法,跟内部类ListItr的add方法是一个道理。

5.面试题:iterator(),Iterator,Iterable关系


1.iterator()是Iterable接口中定义的方法,用于返回一个迭代器(Iterator)对象。

2.Iterator接口是Java集合框架中用于遍历集合元素的标准接口,它定义了一些方法,如hasNext()、next()、remove()等,用于遍历集合中的元素并对其进行操作。

3.Iterable接口是Java集合框架中用于表示“可迭代”的对象的标准接口,它定义了一个iterator()方法,用于返回一个迭代器(Iterator)对象,从而可以遍历集合中的元素。

hashNext()、next()方法源码简化

二、Vector实现类

1. 与ArrayList区别与联系

区别:

  • Vector底层扩容长度为原数组2倍,线程安全,效率低
  • ArrayList底层扩容长度为原数组1.5倍,线程不安全,效率高

联系:

  • 底层都是数组,具有数组的优缺点(查询快,增删慢)
  • 都继承了AbstractList类并实现List接口


add方法源码简化

public class Vector<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{protected Object[] elementData; //数组protected int elementCount;//元素个数protected int capacityIncrement;//扩容数量private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;public Vector() {//创建时默认数组长度为10,ArrayList创建出来的时候是空数组,第一次调用add才会创建长度10的数组this(10); }public Vector(int initialCapacity) {this(initialCapacity, 0);}public Vector(int initialCapacity, int capacityIncrement) {super();this.elementData = new Object[initialCapacity];this.capacityIncrement = capacityIncrement;}public Vector(Collection<? extends E> c) {Object[] a = c.toArray();elementCount = a.length;if (c.getClass() == ArrayList.class) {elementData = a;} else {elementData = Arrays.copyOf(a, elementCount, Object[].class);}}//为什么Vector是线程安全的,因为方法上都有synchronized关键字进行同步public synchronized boolean add(E e) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true;}private void ensureCapacityHelper(int minCapacity) {// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);}private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;//这里就是两倍扩容的代码了,在capacityIncrement一直为0的时候,就是oldCapacity+oldCapacityint 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);}}

三、 LinkedList实现类

1. 简要原理图

2. 源码分析(简化版)

JDK1.7和JDK1.8的LinkedList的源码是一致的

public class LinkedList<E>{//E是一个泛型,具体的类型要在实例化的时候才会最终确定transient int size = 0;//集合中元素的数量//Node的内部类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;}}transient Node<E> first;//链表的首节点ransient Node<E> last;//链表的尾节点//空构造器:public LinkedList() {}//添加元素操作:public boolean add(E e) {linkLast(e);return true;}void linkLast(E e) {//添加的元素efinal Node<E> l = last;//将链表中的last节点给l 如果是第一个元素的话 l为null//将元素封装为一个Node具体的对象:final Node<E> newNode = new Node<>(l, e, null);//将链表的last节点指向新的创建的对象:last = newNode;if (l == null)//如果添加的是第一个节点first = newNode;//将链表的first节点指向为新节点else//如果添加的不是第一个节点 l.next = newNode;//将l的下一个指向为新的节点size++;//集合中元素数量加1操作modCount++;}//获取集合中元素数量public int size() {return size;}//通过索引得到元素:public E get(int index) {checkElementIndex(index);//健壮性考虑return node(index).item;}Node<E> node(int index) {//如果index在链表的前半段,那么从前往后找if (index < (size >> 1)) {Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else {//如果index在链表的后半段,那么从后往前找Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;}}
}

四、其他问题

1.ArrayList源码和Vector源码中的都会有一个成员变量private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,这个数组容量上限MAX_ARRAY_SIZE为什么要-8?

其实源码中成员变量上面就有英文解释,简要回答就是保证跨平台,有些虚拟机数组会有会有保留字,出于安全性和健壮性考虑所以做了长度限制。可参考以下博客说明。
https://blog.csdn.net/qq_44734036/article/details/118982215

为什么是-8可以参考以下博客说明。
https://blog.csdn.net/fisherish/article/details/117134717

实际上ArrayList能不能直接到达Integer.MAX_VALUE这个,我认为是可以的,ArrayList源码扩容函数里面实际是允许的,只是一开始new ArrayList<>(Integer.MAX_VALUE);这个会报错,但是如果进行扩容操作的话能达到。这个只是从源码上进行的推论,没有实际能验证过。
以下为源码说明:

五.Collection部分整体结构图

Java集合——List接口学习总结相关推荐

  1. Java集合——Map接口学习总结

    一.HashMap实现类 1.常用方法 增加:put(K key, V value)删除:clear() remove(Object key)修改:查看:entrySet() get(Object k ...

  2. Java 回调 (Callback) 接口学习使用

    文章目录 Java 回调 (Callback) 接口学习使用 1.什么是回调(Callback)? 2.Java代码示例 2.直接调用 3.接口调用 4.Lambda表达式 推荐看我的InfoQ地址, ...

  3. Java集合Collection接口中的常用方法演示

    Java集合Collection接口中的常用方法演示 添加 add(Objec tobj) 和 addAll(Collection coll) 获取有效元素的个数 int size() 清空集合 vo ...

  4. Java集合与泛型学习笔记

    1:Java集合类 首先我们思考为什么出现集合类? 理由: 1面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象操作,就对对象进行储存,集合就是储存对象最常用的一种方式 2 数组和集合同 ...

  5. JAVA集合Connection接口和Map接口常用的类及区别

    JAVA集合详解 文章目录 JAVA集合详解 前言 一.集合是什么? 1. 集合类 2.集合和数组的区别: 二.使用步骤 一.Connection接口(单列集合) 1.List和Set的优缺点 2.总 ...

  6. java集合框架——接口图+类图+遗留类图

    [0]README 0.1)绝对的干货,理清 java集合框架中类和接口的层次关系:

  7. Java集合这样子学习

    耗时五天学完集合的大致内容,韩顺平老师讲的特别透彻 大家有兴趣可以观看韩老师讲课,特别细节!!! 集合首先需要大家记住下面这张图 一.首先是Collection接口 1.实现了Collection接口 ...

  8. Java集合源码学习(四)HashMap

    一.数组.链表和哈希表结构 数据结构中有数组和链表来实现对数据的存储,这两者有不同的应用场景, 数组的特点是:寻址容易,插入和删除困难:链表的特点是:寻址困难,插入和删除容易: 哈希表的实现结合了这两 ...

  9. Java集合源码学习(五)几种常用集合类的比较

    这篇笔记对几个常用的集合实现,从效率,线程安全和应用场景进行综合比较. 1.ArrayList.LinkedList与Vector的对比 (1)相同和不同 都实现了List接口,使用类似. Vecto ...

最新文章

  1. RNN情感分类问题实战
  2. 以演进式的架构来让系统为变化做好准备
  3. flink sql udf jar包_flink教程flink 1.11 集成zeppelin实现简易实时计算平台
  4. 精通jQuery选择器使用 转一篇
  5. SpringBoot集成Druid不支持多条SQL
  6. python 局部变量 占内存吗_Python中全局变量和局部变量的理解与区别
  7. 机器学习-样本集(包括训练集及测试集)的选取
  8. 推荐系统笔记三、基于近邻的推荐系统进阶篇
  9. 一周最新示例代码回顾 (4/23–4/29)
  10. 软件再快不如自带:找不到电脑文件?教你这 3 个Mac搜索技巧
  11. JAVA学习之网络编程UDP篇
  12. PointTrackNet:一种用于点云三维目标检测和跟踪的端到端网络
  13. PreferenceActivity的使用
  14. idea风格ui java_IDEA主题美化
  15. 抖音视频社交产品多闪上架各大应用市场
  16. stm32 simulink 快速计算 Timmer定时器需要的预分频PSC和自动重载ARR
  17. 华为荣耀5cvs华为v9play_荣耀V9Play和荣耀畅玩6A全面对比评测 看完这篇再判断
  18. 微信公众号数据2019_全国公众号总排名2019,全国微信公众号排名
  19. keypair java_Java KeyPairGenerator generateKeyPair()用法及代码示例
  20. 基于MATLAB-GUI数字图像的水果边缘检测

热门文章

  1. Android 8.0适配指北
  2. 用HTML+CSS+JS做一个漂亮简单的游戏网页——全屏游戏美术大赛作品(4个滚动页面)
  3. 清幽傲竹实现kbmMWServer的方法(转)
  4. 克鲁伊夫:最贵铁卫缘何难融巴萨 瓜帅凭啥遭批(2010-01-12)
  5. 跨平台(uni-app)文件在线预览解决方案
  6. python执行超时退出
  7. 计算机网络基础(三):默认网关
  8. Unity2020 Unity2021 场景灯光烘焙简单教程,Unity场景灯光优化, 一些简单的问题
  9. opencv嵌入式ARM系统中安装
  10. 在19计算机考研炸掉的情况下,21计算机考研的难度会很大吗?