Java List接口、ArrayList、Vector、LinkedList 【源码详解】

List接口

java.util.List接口继承自Collection接口,是单列集合的一个重要分支,习惯性地会将实现了List接口的对象称为List集合

在List集合中允许出现重复的元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引来访问集合中的指定元素。List集合是元素有序的,即元素的存入顺序和取出顺序一致。

List接口特点:

  • 元素存取有序。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的。
  • 带有索引,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)
  • 集合中可以有重复的元素,通过元素的equals方法,来比较是否是重复的元素。

常用方法

List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法

void add(int index, E element):将指定的元素,添加到该集合中的指定位置上。
boolean addAll(int index, Collection<? extends E> c):将指定集合中的所有元素,添加到该集合中的指定位置上。

E get(int index):返回集合中指定位置的元素。

int indexOf(Object o):返回o在集合中首次出现的位置,没有则返回-1。
int lastIndexOf(Object o):返回o在集合中最后一次出现的位置,没有则返回-1。

E remove(int index):移除列表中指定位置的元素,返回的是被移除的元素。

E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。相当于替换。

List<E> subList(int fromIndex, int toIndex):返回从fromIndex位置到toIndex位置的子集合,前闭后开,不包含toIndex位置的元素。

List集合的三种遍历方式

第一种: 迭代器

Iterator iter = list.itertor();
while(iter.hasNext()) {
	Object o = iter.next();
}

第二种: 增强for

for(Object o : list) {
}

第三种: 普通for

for(int i = 0; i < list.size(); i++) {
	Object o = list.get(i);
}

ArrayList

java.util.ArrayList是大小可变的数组的实现,存储在内的数据称为元素。此类提供一些方法来操作内部存储的元素。 ArrayList中可不断添加元素,其大小也自动增长。

ArrayList可以加入null,并且可以多个。

ArrayList基本等同于Vector,但是ArrayList是线程不安全的(执行效率更高),所以在多线程的情况下,不建议使用ArrayList。

// 基本格式:
ArrayList<String> list = new ArrayList<String>();

// 在JDK 7后,右侧泛型的尖括号之内可以留空,但是<>仍然要写。简化格式:
ArrayList<String> list = new ArrayList<>();

常用方法和List接口、Collection接口一样。

public class Demo01ArrayListMethod {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList<String> list = new ArrayList<String>();
        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");
        //public E get(int index):返回指定索引处的元素
        System.out.println("get:"+list.get(0));
        System.out.println("get:"+list.get(1));
        System.out.println("get:"+list.get(2));
        //public int size():返回集合中的元素的个数
        System.out.println("size:"+list.size());
        //public E remove(int index):删除指定索引处的元素,返回被删除的元素
        System.out.println("remove:"+list.remove(0));
        //遍历输出
        for(int i = 0; i < list.size(); i++){
            System.out.println(list.get(i));
        }
    }
}

【源码】扩容机制

ArrayList中维护了一个Object类型的数组elementData
transient关键字:表示瞬间、短暂的,改属性不会被序列化

在这里插入图片描述

当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加时,扩容为10,如需再次扩容,则扩容为elementData的1.5倍。
① 当new一个ArrayList对象的时候会进入空参构造,并初始化一个长度为0的空数组。
② 调用add方法时,会先确定是否需要扩容,然后再将元素存入集合中。

在这里插入图片描述
在这里插入图片描述
后面再次扩容时,就是按照1.5倍扩容。然后将原数组的数据,使用Arrays.copyOf()方法拷贝到新的数组中。
在这里插入图片描述

如果使用的是指定大小的构造器,则初始elementData容量为指定的大小,如需扩容,则直接扩容elementData的1.5倍。
① 当new一个ArrayList对象的时候会进入有参构造,并初始化一个长度为传入的大小的数组。
② 这里就和上面的一样了,调用add方法时,会先确定是否需要扩容,然后再将元素存入集合中。

Vector

在这里插入图片描述

Vector底层也是一个对象数组,protected Object[] elementData;
Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized关键字。
所以,在开发中,需要线程同步安全时,使用Vector。

扩容机制与ArrayList类似。

Vector和ArrayList的比较

底层结构版本线程安全(同步)、效率扩容机制
ArrayList可变数组JDK 1.2不安全,效率高如果有参构造1.5倍
如果无参构造:
1. 第一次是10
2. 从第二次扩容开始,按1.5倍
Vector可变数组JDK 1.0安全,效率不高如果无参构造,默认10,第二次开始按2倍扩容
如果有参构造,每次按2倍扩容

LinkedList

java.util.LinkedList集合数据存储的结构是链表结构,方便元素添加、删除的集合,LinkedList是一个双向链表。

可以添加任意元素,并且可以重复,包括null。

线程不安全,没有实现同步。

所以LinkedList提供了大量首尾操作的方法。LinkedList是List的子类,List中的方法LinkedList都是可以使用,LinkedList集合也可以作为堆栈、队列的结构使用。

常用方法

因为LinkedList实现了List接口,所以List接口中的所有方法LinkedList也可以使用,这里列出了LinkedList常用的独有的方法。

void addFirst(E e):将指定元素插入此列表的开头
void addLast(E e):将指定元素添加到此列表的结尾

E getFirst():返回此列表的第一个元素
E getLast():返回此列表的最后一个元素

E removeFirst():移除并返回此列表的第一个元素
E removeLast():移除并返回此列表的最后一个元素

E pop():从此列表所表示的堆栈处弹出一个元素

void push(E e):将元素推入此列表所表示的堆栈

boolean isEmpty():如果列表不包含元素,则返回true

模拟简单链表

LinkedList中维护了两个属性,firstlast分别指向了首节点和尾结点,每个节点(Node对象),里面又维护了prevnextitem三个属性,其中通过prev指向前一个,通过next后一个,最终实现双向链表。

因为LinkedList的元素添加和删除不是通过数组完成的,所以效率相对来说较高。

在这里插入图片描述

定义一个Node类:

//定义一个Node类,Node对象表示双向链表的一个节点
class Node {
    // 存放的数据
    public Object item;
    // 下一个节点
    public Node next;
    // 上一个节点
    public Node prev;

    public Node(Object item) {
        this.item = item;
    }
    
    @Override
    public String toString() {
        return "Node item = " + item;
    }
}

测试类:使用双向链表

public class LinkedList01 {
    public static void main(String[] args) {
        // 模拟一个双向链表
        Node tom = new Node("tom");
        Node jack = new Node("jack");
        Node arbor = new Node("arbor");

        // 连接三个节点,形成双向链表
        // tom → jack → arbor
        tom.next = jack;
        jack.next = arbor;

        // arbor → jack → tom
        arbor.prev = jack;
        jack.prev = tom;

        // 定义一个头节点,指向tom
        Node first = tom;
        // 定义一个尾结点,指向arbor
        Node last = arbor;

        // 链表添加节点 在jack和arbor之间插入zhangsan节点
        // 因为没有数组,不用扩容和拷贝,所以增删更快
        Node zhangsan = new Node("zhangsan");
        zhangsan.prev = jack;
        zhangsan.next = arbor;
        jack.next = zhangsan;
        arbor.prev = zhangsan;

        // 链表删除节点 删除jack节点
        tom.next = zhangsan;
        zhangsan.prev = tom;

        // 使用双向链表,从头节点到尾节点遍历
        while (true) {
            if (first == null) {
                break;
            }
            System.out.println(first);
            first = first.next;
        }

        System.out.println("-------------------");

        // 使用双向链表,从尾节点到头节点遍历
        while (true) {
            if (last == null) {
                break;
            }
            System.out.println(last);
            last = last.prev;
        }
    }
}

LinkedList源码

add方法

当使用空参构造new一个LinkedList时,空参构造是没有语句执行的。
在这里插入图片描述
此时,长度为0,第一个节点和最后一个节点都是null。
在这里插入图片描述
当程序走到linkedList.add(1);时,走的是下面这个方法:
在这里插入图片描述
进入linkLast方法,进行以下分析:
在这里插入图片描述

再次执行add方法,新增加一个节点,又会进入linkLast方法:
在这里插入图片描述

remove方法

调用空参的remove()方法,会删除链表的第一个节点。
在这里插入图片描述
进入removeFirst()方法可以看到,会先判断first节点是否为空(也就是判断是否为空链表),如果不是空的话,进行删除:在这里插入图片描述
通过分析
在这里插入图片描述

ArrayList和LinkedList的比较

底层结构增删的效率改查的效率
ArrayList可变数组较低,扩容数组较高
LinkedList双向链表较高,链表追加较低

如果改查多,选择ArrayList
如果增删多,选择LinkedList

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VincentHu_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值