第一篇文章 「 深入浅出 」java集合Collection和Map 主要讲了对集合的整体介绍,本篇文章主要讲List相对于Collection新增的一些重要功能以及其重要子类ArrayList、LinkedList、Vector

一、List集合

关于List集合的介绍与方法,可参考第一篇文章 「 深入浅出 」java集合Collection和Map

迭代方法ListIterator

相对于其它集合,List集合添加了一种新的迭代方法ListIterator
ListIterator的方法如下:

ListIterator接口在Iterator接口基础上增加了如下方法:
boolean hasPrevious(): 如果以逆向遍历列表。如果迭代器有上一个元素,则返回 true。
E previous():返回迭代器的前一个元素。
void add(Object o):将指定的元素插入列表。
int nextIndex():下一个索引号
int previousIndex():上一个索引号
void set(E e):修改迭代器当前元素的值
void add(E e):在迭代器当前位置插入一个元素

ListIterator接口比Iterator接口多了两个功能:
1.ListIterator可在遍历过程中新增和修改
2.ListIterator可逆向遍历

使用示例如下:

public class ListIteratorDemo {public static void main(String[] args) {// 创建列表List<Integer> list = new ArrayList<Integer>();// 向列表中增加10个元素for (int i = 0; i < 10; i++) {list.add(i);}// 获得ListIterator对象ListIterator<Integer> it = list.listIterator();// 正序遍历修改与新增while (it.hasNext()) {Integer i = it.next();//修改元素值it.set(i+1);if(i == 5 ){//新增元素值it.add(55);}//! it.set(i+1);// 注意:如果修改的代码在这个位置会报错//set操作不能放在add操作之后// 这里不做解析,欲知详情,请看源码}System.out.println("正向遍历");//正向遍历for(Integer i:list){System.out.println(i+" ");}System.out.println("逆向遍历");//逆向遍历//经过上面迭代器it遍历后,迭代器it已到达最后一个节点while (it.hasPrevious()) {System.out.println(it.previous() + " ");}}
}

二、ArrayList和Vector

ArrayList和Vector很相似,所以就一起介绍了

ArrayList和Vector类都是基于数组实现的List类,所以ArrayList和Vector类封装了一个动态的、允许再分配的Object[]数组。ArrayList和Vector对象使用initalCapacity参数来设置该数组的长度,当向ArrayList和Vector中添加元素超过了该数组的长度时,它们的initalCapacity会自动增加。

下面我们通过阅读JDK 1.8 ArrayList源码来了解ArrayList

无参构造函数

默认初始化为容量为10

   /*** Constructs an empty list with an initial capacity of ten。意思是:构造一个空数组,默认的容量为10*/public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}

有参构造函数

创建指定容量的ArrayList

//动态Object数组,用来保存加入到ArrayList的元素
Object[] elementData;//ArrayList的构造函数,传入参数为数组大小
public ArrayList(int initialCapacity) {if (initialCapacity > 0) {//创建一个对应大小的数组对象this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {//传入数字为0,将elementData 指定为一个静态类型的空数组this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}

add方法

执行add方法时,先确保容量足够大,若容量不够,则会进行扩容;
扩容大小为原来的1.5倍(这个需要注意一下,面试经常考)

//添加元素e
public boolean add(E e) {ensureCapacityInternal(size + 1);//将对应索引下的元素赋值为e:elementData[size++] = e;return true;
}
//得到最小扩容量
private void ensureCapacityInternal(int minCapacity) {//如果此时ArrayList是空数组,则将最小扩容大小设置为10:if (elementData == EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}//判断是否需要扩容:ensureExplicitCapacity(minCapacity);
}
//判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {//操作数+1modCount++;//判断最小扩容容量-数组大小是否大于0:if (minCapacity - elementData.length > 0)//扩容:grow(minCapacity);
}
//ArrayList动态扩容的核心方法:
private void grow(int minCapacity) {//获取现有数组大小:int oldCapacity = elementData.length;//位运算,得到新的数组容量大小,为原有的1.5倍:int newCapacity = oldCapacity + (oldCapacity >> 1);//如果新扩容的大小依旧小于传入的容量值,那么将传入的值设为新容器大小:if (newCapacity - minCapacity < 0)newCapacity = minCapacity;//如果新容器大小,大于ArrayList最大长度:if (newCapacity - MAX_ARRAY_SIZE > 0)//计算出最大容量值:newCapacity = hugeCapacity(minCapacity);//数组复制:elementData = Arrays.copyOf(elementData, newCapacity);
}
//计算ArrayList最大容量:
private static int hugeCapacity(int minCapacity) {if (minCapacity < 0)throw new OutOfMemoryError();//如果新的容量大于MAX_ARRAY_SIZE//将会调用hugeCapacity将int的最大值赋给newCapacityreturn (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}

remove方法

有以下两种删除方法:

  • remove(int index)是针对于索引来进行删除,不需要去遍历整个集合,效率更高;
  • remove(Object o)是针对于对象来进行删除,需要遍历整个集合进行equals()方法比对,所以效率较低;

不过,无论是哪种形式的删除,最终都会调用System.arraycopy()方法进行数组复制操作,等同于移动数组位置,所以效率都会受到影响

//在ArrayList的移除index位置的元素
public E remove(int index) {//检查索引是否合法:不合法抛异常rangeCheck(index);//操作数+1:modCount++;//获取当前索引的value:E oldValue = elementData(index);//获取需要删除元素 到最后一个元素的长度,也就是删除元素后,后续元素移动的个数;int numMoved = size - index - 1;//如果移动元素个数大于0 ,也就是说删除的不是最后一个元素:if (numMoved > 0)// 将elementData数组index+1位置开始拷贝到elementData从index开始的空间System.arraycopy(elementData, index+1, elementData, index, numMoved);//size减1,并将最后一个元素置为nullelementData[--size] = null;//返回被删除的元素:return oldValue;
}//在ArrayList的移除对象为O的元素,不返回被删除的元素:
public boolean remove(Object o) {//如果o==null,则遍历集合,判断哪个元素为null:if (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {//快速删除,和前面的remove(index)一样的逻辑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) {//操作数+1modCount++;//获取需要删除元素 到最后一个元素的长度,也就是删除元素后,后续元素移动的个数;int numMoved = size - index - 1;//如果移动元素个数大于0 ,也就是说删除的不是最后一个元素:if (numMoved > 0)// 将elementData数组index+1位置开始拷贝到elementData从index开始的空间System.arraycopy(elementData, index+1, elementData, index, numMoved);//size减1,并将最后一个元素置为nullelementData[--size] = null;
}

get方法

通过elementData()方法获取对应索引元素,在返回时候进行类型转换

//获取index位置的元素
public E get(int index) {//检查index是否合法:rangeCheck(index);//获取元素:return elementData(index);
}
//获取数组index位置的元素:返回时类型转换
E elementData(int index) {return (E) elementData[index];
}

set方法

通过elementData获取旧元素,再设置新元素值相应index位置,最后返回旧元素

//设置index位置的元素值了element,返回该位置的之前的值
public E set(int index, E element) {//检查index是否合法:判断index是否大于sizerangeCheck(index);//获取该index原来的元素:E oldValue = elementData(index);//替换成新的元素:elementData[index] = element;//返回旧的元素:return oldValue;
}

调整容量大小

ArrayList还提供了两个额外的方法来调整其容量大小

  • void ensureCapacity(int minCapacity): 增加容量,以确保它至少能够容纳最小容量参数所指定的元素数。
  • void trimToSize():将容量调整为列表的当前大小。

Vector实现原理与ArrayList基本相同,可参考上述内容

ArrayList和Vector的主要区别

  • ArrayList是线程不安全的,Vector是线程安全的。
  • Vector的性能比ArrayList差。

LinkedList

LinkedList是基于双向链表实现的,内部存储主要是Node对象,该对象存储着元素值外,还指向上一节点和下一节点。
注意,因为LinkedList是基于链表实现的,没有容量的说法,所以更没有扩容之说

集合基础框架

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable {//LinkedList的元素个数:transient int size = 0;//LinkedList的头结点:Node内部类transient java.util.LinkedList.Node<E> first;//LinkedList尾结点:Node内部类transient java.util.LinkedList.Node<E> last;//空实现:头尾结点均为null,链表不存在public LinkedList() {}//调用添加方法:public LinkedList(Collection<? extends E> c) {this();addAll(c);}//节点的数据结构,包含前后节点的引用和当前节点private static class Node<E> {//结点元素:E item;//结点后指针java.util.LinkedList.Node<E> next;//结点前指针java.util.LinkedList.Node<E> prev;Node(java.util.LinkedList.Node<E> prev, E element, java.util.LinkedList.Node<E> next) {this.item = element;this.next = next;this.prev = prev;}}
}
LinkedList的常用方法只做简单介绍不贴源码,具体可自行看源码

add方法

LinkedList有两种添加方法

  • add(E e)链表最后添加一个元素
  • add(int index, E element)指定位置下添加一个元素;

LinedList添加元素主要分为以下步骤:
1.将添加的元素转换为LinkedList的Node对象节点;
2.增加该Node节点的前后引用,即该Node节点的prev、next属性,让其分别指上、下节点;
3.修改该Node节点的前后Node节点中pre/next属性,使其指向该节点。

remove方法

LinkedList的删除也提供了2种形式

  • remove(int index)直接通过索引删除元素
  • remove(Object o)通过对象删除元素,需要逐个遍历LinkedList的元素,重复元素只删除第一个:

删除后,需要修改上节点的next指向当前下一节点,下节点的prev指向当前上一节点

set方法

set(int index, E element)方法通过node(index)获取到相应的Node,再修改元素的值

get方法

这是我们最常用的方法,其中核心方法node(int index),需要从头遍历或从后遍历找到相应Node节点
在通过node(int index)获取到对应节点后,返回节点中的item属性,该属性就是我们所保存的元素。

//获取相应角标的元素:
public E get(int index) {//检查索引是否正确:checkElementIndex(index);//获取索引所属结点的 元素值:return node(index).item;
}
//获取对应角标所属于的结点:
java.util.LinkedList.Node<E> node(int index) {//位运算:如果位置索引小于列表长度的一半,则从头开始遍历;否则,从后开始遍历;if (index < (size >> 1)) {java.util.LinkedList.Node<E> x = first;//从头结点开始遍历:遍历的长度就是index的长度,获取对应的index的元素for (int i = 0; i < index; i++)x = x.next;return x;} else {//从集合尾结点遍历:java.util.LinkedList.Node<E> x = last;//同样道理:for (int i = size - 1; i > index; i--)x = x.prev;return x;}
}

ArrayList和LinkedList的主要区别

  • ArrayList基于数组实现的,LinkedList是基于双向链表实现的
  • ArrayList随机访问效率高,随机插入、随机删除效率低,需要移动元素位置
    LinkedList随机插入、随机删除效率高,随机访问效率低,因需要遍历链表

好叻,搞完,溜了溜了
下一期为<集合Set>,敬请期待

近期推荐:

好人?坏人?做真实的人
「 优质资源 」收藏!最新精选优质资源!
java小心机(5)| 浅谈类成员初始化顺序

更多精彩内容,可阅读原文

您的点赞、转发是对我最大的支持!

THANDKS

  • End -

一个立志成大腿而每天努力奋斗的年轻人

伴学习伴成长,成长之路你并不孤单!

「 深入浅出 」集合List相关推荐

  1. java 先进先出的map_「 深入浅出 」java集合Collection和Map

    本系列文章主要对java集合的框架进行一个深入浅出的介绍,使大家对java集合有个深入的理解. 本篇文章主要具体介绍了Collection接口,Map接口以及Collection接口的三个子接口Set ...

  2. python建立空集合_「python」集合类型及操作

    目录: 集合类型定义 集合操作符 集合处理方法 集合类型应用场景 1 集合类型定义 集合是多个元素的无序组合 集合用大括号 {} 表示,元素间用逗号分隔 建立集合类型用 {} 或 set() 建立空集 ...

  3. 「深入浅出」了解语音识别的技术原理和应用价值?

    2019-04-15 20:34:55 语音识别是什么?他有什么价值,以及他的技术原理是什么?本文将解答大家对语音识别的常见疑问. 语音识别技术(ASR)是什么? 机器要与人实现对话,那就需要实现三步 ...

  4. 「深入浅出」主流前端框架更新批处理方式

    作者 |

  5. 4.5管道实现机制和模拟构建管道「深入浅出ASP.NET Core系列」

    原文:4.5管道实现机制和模拟构建管道「深入浅出ASP.NET Core系列」 希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,谢谢关注. 管道实现机制 要了解管道的实现机制 ...

  6. 5.3Role和Claims授权「深入浅出ASP.NET Core系列」

    5.3Role和Claims授权「深入浅出ASP.NET Core系列」 原文:5.3Role和Claims授权「深入浅出ASP.NET Core系列」 希望给你3-5分钟的碎片化学习,可能是坐地铁. ...

  7. 4.1ASP.NET Core请求过程「深入浅出ASP.NET Core系列」

    原文:4.1ASP.NET Core请求过程「深入浅出ASP.NET Core系列」 希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,谢谢关注. HTTP请求过程 这里展示整 ...

  8. 5.1基于JWT的认证和授权「深入浅出ASP.NET Core系列」

    原文:5.1基于JWT的认证和授权「深入浅出ASP.NET Core系列」 希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,码字辛苦,如果你吃了蛋觉得味道不错,希望点个赞,谢 ...

  9. java堆与非堆的一些研究_堆和堆傻傻分不清?一文告诉你 Java 集合中「堆」的最佳打开方式...

    什么是堆? 堆其实就是一种特殊的队列--优先队列. 普通的队列游戏规则很简单:就是先进先出:但这种优先队列搞特殊,不是按照进队列的时间顺序,而是按照每个元素的优先级来比拼,优先级高的在堆顶. 这也很容 ...

最新文章

  1. Django models的诡异异常RelatedObjectDoesNotExist
  2. android 双 webview,Android webview加载页面
  3. lisp 绘制立体感的五角星_[原创]圆内加五角星lsp代码,详细有注解
  4. 高仿真的类-BeanWrapper
  5. [Leetcode][第60题][JAVA][第k个排列][回溯][DFS][剪枝]
  6. l4 l7 代理_什么是四层(L4 proxy)和七层负载均衡(L7 proxy)?区别是什么? 翻译自Nginx官网...
  7. QML笔记-自定义控件的2种点击方式(推荐第二种)
  8. Ajax+SpringMVC+JSON登录验证
  9. Java基础整理(二)
  10. “”和“”的你真的理解吗?
  11. 课后作业1:字串加密
  12. 根据Java源码生成流程图
  13. 基于Javaweb实现小区物业管理系统
  14. java计算机毕业设计雁门关风景区宣传网站源码+系统+mysql数据库+lw文档
  15. SQL语句中不完整数据的补足处理技巧
  16. docker安装memos
  17. 2w字Python列表,全了!
  18. Latex基础命令入门
  19. 亲手搭建vue项目的过程--Module build failed: Unknown word,无法读取css文件
  20. 基于IOCP的局域网监控系统

热门文章

  1. 白话Elasticsearch05- 结构化搜索之使用range query来进行范围过滤
  2. idea 2019.2 版本更新(最顶部从白色边框变为黑色边框)
  3. Gradle自动化之自动打包并上传到fir测试网站
  4. mysql删除账户后不能使用_MySQL删除普通用户
  5. 跟alex学python_跟着Alex学习python
  6. 微信小程序-canvas绘制文字实现自动换行
  7. 2021-02-28 Matlab绘制短时傅里叶变换的频谱图和时间-频率-幅值三维图
  8. 通过python里面的pyautogui自动化控制鼠标和键盘操作
  9. node process.env
  10. 实施工程师常用linux命令,009Linux管理日常使用的基本命令