本篇博客主要讲解List接口的三个实现类ArrayList、LinkedList、Vector的使用方法以及三者之间的区别。

注意:本文中代码使用的JDK版本为1.8.0_191

1. ArrayList使用

ArrayList是List接口最常用的实现类,内部通过数组来实现,因此它的优点是适合随机查找和遍历,缺点是不适合插入和删除。

ArrayList类的代码声明如下所示:

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
}

1.1 添加元素

使用ArrayList添加元素有以下两个重载:

boolean add(E e);

void add(int index, E element);

boolean add(E e);是将元素添加到集合的末尾,

void add(int index, E element);是将元素添加到指定的索引位置(索引是从0开始的)。

使用方法如下所示:

List<String> platformList = new ArrayList<>();

// 添加元素
platformList.add("博客园");
platformList.add("掘金");
platformList.add("微信公众号"); // 添加重复元素,会添加成功,因为List支持添加重复元素
platformList.add("博客园");
platformList.add("掘金"); platformList.add(3, "个人博客");

1.2 获取元素

获取ArrayList中指定索引处的元素的使用方法如下所示:

System.out.println("索引为3的元素为:" + platformList.get(3));

如果指定的索引超出了集合的最大索引,比如platformList.get(6)就会抛出异常java.lang.IndexOutOfBoundsException

1.3 获取集合元素个数

获取ArrayList元素个数的使用方法如下所示:

System.out.println("platformList的元素个数为:" + platformList.size());

1.4 删除元素

使用ArrayList删除元素有以下两个重载:

E remove(int index);

boolean remove(Object o);

E remove(int index);是删除集合中指定索引处的元素,boolean remove(Object o);是删除集合中的指定元素。

使用方法如下所示:

// 指定索引删除重复的元素 "博客园" "掘金"
platformList.remove(4);
platformList.remove(4);
// 删除指定元素 "个人博客"
platformList.remove("个人博客");

1.5 修改元素

修改ArrayList中指定索引处的元素值的使用方法如下所示:

platformList.set(0, "博客园:https://www.cnblogs.com/zwwhnly/");
platformList.set(1, "掘金:https://juejin.im/user/5c7ce730f265da2dca388167");
platformList.set(2, "微信公众号:申城异乡人");

1.6 判断集合是否为空

判断ArrayList是否为空的使用方法如下所示:

System.out.println("isEmpty:" + platformList.isEmpty());

1.7 遍历元素(面试常问)

遍历ArrayList的元素主要有以下3种方式:

  1. 迭代器遍历
  2. for循环
  3. foreach循环

使用方法如下所示:

System.out.println("使用Iterator遍历:");
Iterator<String> platformIterator = platformList.iterator();
while (platformIterator.hasNext()) {
System.out.println(platformIterator.next());
} System.out.println();
System.out.println("使用for循环遍历:");
for (int i = 0; i < platformList.size(); i++) {
System.out.println(platformList.get(i));
} System.out.println();
System.out.println("使用foreach遍历:");
for (String platform : platformList) {
System.out.println(platform);
}

1.8 清空集合

清空ArrayList中所有元素的使用方法如下所示:

platformList.clear();

1.9 完整示例代码

上面讲解的几点,完整代码如下所示:

public static void main(String[] args) {
List<String> platformList = new ArrayList<>(); // 添加元素
platformList.add("博客园");
platformList.add("掘金");
platformList.add("微信公众号"); // 添加重复元素,会添加成功,因为List支持添加重复元素
platformList.add("博客园");
platformList.add("掘金"); platformList.add(3, "个人博客"); System.out.println("索引为3的元素为:" + platformList.get(3));
System.out.println("platformList的元素个数为:" + platformList.size()); // 指定索引删除重复的元素 "博客园" "掘金"
platformList.remove(4);
platformList.remove(4);
// 删除指定元素 "个人博客"
platformList.remove("个人博客"); System.out.println("platformList的元素个数为:" + platformList.size()); platformList.set(0, "博客园:https://www.cnblogs.com/zwwhnly/");
platformList.set(1, "掘金:https://juejin.im/user/5c7ce730f265da2dca388167");
platformList.set(2, "微信公众号:申城异乡人"); System.out.println("isEmpty:" + platformList.isEmpty()); System.out.println("使用Iterator遍历:");
Iterator<String> platformIterator = platformList.iterator();
while (platformIterator.hasNext()) {
System.out.println(platformIterator.next());
} System.out.println();
System.out.println("使用for循环遍历:");
for (int i = 0; i < platformList.size(); i++) {
System.out.println(platformList.get(i));
} System.out.println();
System.out.println("使用foreach遍历:");
for (String platform : platformList) {
System.out.println(platform);
} System.out.println(); // 清空集合
platformList.clear();
System.out.println("isEmpty:" + platformList.isEmpty());
}

输出结果为:

索引为3的元素为:个人博客

platformList的元素个数为:6

platformList的元素个数为:3

isEmpty:false

使用Iterator遍历:

博客园:https://www.cnblogs.com/zwwhnly/

掘金:https://juejin.im/user/5c7ce730f265da2dca388167

微信公众号:申城异乡人

使用for循环遍历:

博客园:https://www.cnblogs.com/zwwhnly/

掘金:https://juejin.im/user/5c7ce730f265da2dca388167

微信公众号:申城异乡人

使用foreach遍历:

博客园:https://www.cnblogs.com/zwwhnly/

掘金:https://juejin.im/user/5c7ce730f265da2dca388167

微信公众号:申城异乡人

isEmpty:true

2. LinkedList使用

LinkedList也是List接口的实现类,内部使用链表结构存储数据,因此它的优点是适合动态插入和删除元素,缺点是随机查找和遍历速度比较慢。

LinkedList类的代码声明如下所示:

public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
}

LinkedList类的使用方法和ArrayList基本一样,只需修改下声明处的代码即可:

List<String> platformList = new LinkedList<>();

3. Vector使用

Vector也是List接口的实现类,内部也是通过数组来实现。

Vector类的代码声明如下所示:

public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
}

与ArrayList不同的是,Vector是线程安全的,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性。不过这也造成Vector的缺点:实现线程的同步需要额外的花费,因此它的访问速度会比ArrayList慢一些。

可以认为Vector是ArrayList在多线程环境下的实现版本。

所以Vector类的使用方法和ArrayList基本一样,只需修改下声明处的代码即可:

List<String> platformList = new Vector<>();

由于要支持线程同步,因此Vector类的很多方法都有synchronized关键字,如下所示:

public synchronized boolean isEmpty() {
return elementCount == 0;
} public synchronized int size() {
return elementCount;
} public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}

4. ArrayList、LinkedList、Vector的区别(面试常问)

4.1 相同点

ArrayList、LinkedList、Vector都实现了List接口,所以使用方式很类似,通过上面的示例也能发现这一点。

4.2 不同点

但是ArrayList、LinkedList、Vector的内部实现方式不同,也就导致了它们之间是有区别的。

4.2.1 存储结构

ArrayList和Vector是基于数组实现的,LinkedList是基于双向链表实现的。

这也就导致ArrayList适合随机查找和遍历,而LinkedList适合动态插入和删除元素。

关于数组和双向链表,这里不做详解,后续会单独写篇文章总结。

4.2.2 线程安全性

ArrayList和LinkedList是线程不安全的,Vector是线程安全的。

Vector可以看做是ArrayList在多线程环境下的另一种实现方式,这也导致了Vector的效率没有ArraykList和LinkedList高。

如果要在并发环境下使用ArrayList或者LinkedList,可以调用Collections类的synchronizedList()方法:

Collections.synchronizedList(platformList);

4.2.3 扩容机制

ArrayList和Vector都是使用Object类型的数组来存储数据的,ArrayList的默认容量是0,Vector的默认容量是10。

空说无凭,我们先看下ArrayList的使用示例:

List<String> strArrayList = new ArrayList<>();

for (int i = 0; i < 20; i++) {
strArrayList.add(String.valueOf(i));
}

执行的ArrayList构造函数的源码为:

transient Object[] elementData;

public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
} private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

再看下Vector的使用示例:

List<String> strVector = new Vector<>();

for (int i = 0; i < 30; i++) {
strVector.add(String.valueOf(i));
}

执行的Vector构造函数的源码为:

protected Object[] elementData;
protected int capacityIncrement; public Vector() {
this(10);
} public Vector(int initialCapacity) {
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;
}

当向这两种类型中添加元素时,若容量不够,就会进行扩容,扩容的本质是产生一个新数组,将原数组的数据复制到新数组,再将新的元素添加到新数组中,使用的方法是Arrays.copyOf(),其中ArrayList扩容后的容量是之前的1.5倍,Vector默认情况下扩容后的容量是之前的2倍

仍然使用上面的ArrayList的例子:

List<String> strArrayList = new ArrayList<>();

for (int i = 0; i < 20; i++) {
strArrayList.add(String.valueOf(i));
}

在执行完List<String> strArrayList = new ArrayList<>();后,此时strArrayList的容量是0,

然后在添加第1个元素时,strArrayList的容量会扩容为容量10,

当添加第11个元素时,strArrayList的容量会扩容为容量15,

当添加第16个元素时,strArrayList的容量会扩容为容量22,

如果还需要扩容,依次会扩容到33-->49。

看下ArrayList的源码,就明白为什么会这样扩容:

private static final int DEFAULT_CAPACITY = 10;

public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
} private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
} private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
} private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
} private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}

最核心的代码就是int newCapacity = oldCapacity + (oldCapacity >> 1);,所以ArrayList扩容后的容量是之前的1.5倍。

再看下上面的Vector例子:

List<String> strVector = new Vector<>();

for (int i = 0; i < 30; i++) {
strVector.add(String.valueOf(i));
}

在执行完List<String> strVector = new Vector<>();后,此时strVector的容量是10,

当添加第11个元素时,strVector的容量会扩容为容量20,

当添加第21个元素时,strVector的容量会扩容为容量40,

如果还需要扩容,依次会扩容到80-->160。

看下Vector的源码,就明白为什么会这样扩容:

public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
} private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
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);
}

最核心的代码就是int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);,所以Vector默认情况下扩容后的容量是之前的2倍。

4.2.4 效率

ArrayList随机查找和遍历的效率会高一些,但动态插入和删除元素的效率会低一些。

LinkedList动态插入和删除元素的效率会高一些,但随机查找和遍历的效率会低一些。

如果需要在多线程下操作集合元素,建议使用Vector,否则的话,建议使用ArrayList。

5. 源码及参考

ArrayList、LinkedList、Vector的区别和实现原理

Java深入 - 深入理解Java集合

Java进阶(四十六)简述ArrayList、Vector与LinkedList的异同点

原创不易,如果觉得文章能学到东西的话,欢迎点个赞、评个论、关个注,这是我坚持写作的最大动力。

如果有兴趣,欢迎添加我的微信:zwwhnly,等你来聊技术、职场、工作等话题(PS:我是一名奋斗在上海的程序员)。

Java集合系列(二):ArrayList、LinkedList、Vector的使用方法及区别的更多相关文章

  1. 【Java集合系列二】LinkedList解析

    一.简介 1.LinkedList继承关系 2.LinkedList底层实现 LinkedList使用双向链表存储数据,所以没有默认的容量,也不会有扩容一说.只有两个指针,永远指向链表的两端:firs ...

  2. Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  3. java集合系列之ArrayList源码分析

    java集合系列之ArrayList源码分析(基于jdk1.8) ArrayList简介 ArrayList时List接口的一个非常重要的实现子类,它的底层是通过动态数组实现的,因此它具备查询速度快, ...

  4. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  5. java集合系列之三(ArrayList)

    上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解ArrayLi ...

  6. 【Java集合系列】---ArrayList

    开篇前言--ArrayList中的基本方法 前面的博文中,小编主要简单介绍java集合的总体架构,在接下来的博文中,小编将详细介绍里面的各个类,通过demo.对比,来对java集合类进行更加深入的理解 ...

  7. 【转】Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  8. Java——集合框架之ArrayList,LinkedList,迭代器Iterator

    概述--集合框架 Java语言的设计者对常用的数据结构和算法做了一些规范(接口)和实现(具体实现接口的类).所有抽象出来的数据结构和操作(算法)统称为Java集合框架(Java Collection ...

  9. Java集合系列[1]----ArrayList源码分析

    本篇分析ArrayList的源码,在分析之前先跟大家谈一谈数组.数组可能是我们最早接触到的数据结构之一,它是在内存中划分出一块连续的地址空间用来进行元素的存储,由于它直接操作内存,所以数组的性能要比集 ...

随机推荐

  1. Nio编程模型总结

    终于,这两天的考试熬过去了, 兴致冲冲的来整理笔记来, 这篇博客是我近几天的NIO印象笔记汇总,记录了对Selector及Selector的重要参数的理解,对Channel的理解,常见的Channel ...

  2. Dynamics 365中的事件框架与事件执行管道(Event execution pipeline)

    本文介绍了Microsoft Dynamics 365(以下简称D365)中的两个概念,事件框架(Event Framework)与事件执行管道(Event execution pipeline). ...

  3. Asp.Net Core 项目 EntityFramework Core 根据登录用户名过滤数据

    1.创建ASP.NET Core Web Applicatoin (MVC)项目,并且使用 Individual User Account 2.创建数据筛选接口 Models->IDataFil ...

  4. 02 我的第一个Javascript代码

    02-第一个JavaScript代码   在页面中,我们可以在body标签中放入<script type=”text/javascript”></script>标签对儿,< ...

  5. spring boot + druid + mybatis + atomikos 多数据源配置 并支持分布式事务

    文章目录 一.综述 1.1 项目说明 1.2 项目结构 二.配置多数据源并支持分布式事务 2.1 导入基本依赖 2.2 在yml中配置多数据源信息 2.3 进行多数据源的配置 三.整合结果测试 3.1 ...

  6. java源码解析之String类(四)

    /* * 返回指定字符第一次出现的字符串内的索引 */ public int indexOf(int ch) { return indexOf(ch, 0); } /* * 返回指定字符第一次出现的字 ...

  7. 用.NET Core实现一个类似于饿了吗的简易拆红包功能

      需求说明 以前很讨厌点外卖的我,最近中午经常点外卖,因为确实很方便,提前点好餐,算准时间,就可以在下班的时候吃上饭,然后省下的那些时间就可以在中午的时候多休息一下了. 点餐结束后,会有一个好友分享 ...

  8. Flutter学习笔记(3)--Dart变量与基本数据类型

    一.变量 在Dart里面,变量的声明使用var.Object或Dynamic关键字,如下所示: var name = ‘张三’: 在Dart语言里一切皆为对象,所以如果没有将变量初始化,那么它的默认值 ...

  9. Vs连接Mysql数据库

    Vs连接Mysql数据库步骤 1. 首先下载mysql数据库,安装,建库建表 https://www.yiibai.com/mysql/getting-started-with-mysql-store ...

  10. Java_Set用法总结

    在java语言中,提供多种不同的结构来组织对象,Set(集合)是其中的一种,本身是一个接口,其迭代时的顺序取决于其具体实现. 典型的实现包括: HashSet:哈希表是通过使用称为散列法的机制来存储信 ...