1、List(ArrayList、Vector、LinkedList)

本文深入探讨了Java集合框架中的ArrayList、Vector和LinkedList。ArrayList基于动态数组实现,适合随机访问,但插入操作可能导致元素移动;Vector是线程安全的ArrayList,扩容更快;LinkedList作为双向链表,插入高效但随机访问性能较差。文章还详细分析了它们的源码,包括扩容机制和操作细节,并介绍了如何在数组与List之间转换,以及Array与ArrayList的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、List(线性结构)

在这里插入图片描述

在这里插入图片描述

ArrayList Object[] 数组实现,默认大小为 10 ,支持随机访问,连续内存空间,

插入末尾时间复杂度 o(1),插入第 i 个位置时间复杂度 o(n - i)。

扩容,大小变为 1.5 倍,Arrays.copyOf(底层 System.ArrayCopy),复制到新数组,指针指向新数组。

Vector 类似 ArrayList,线程安全,扩容默认增长为原来的 2 倍,还可以指定增长空间长度。

LinkedList 基于链表实现,1.7 为双向链表,1.6 为双向循环链表,取消循环更能分清头尾。

1.2、遍历一个list有哪些方式?

在这里插入图片描述

1.3、ArrayList

1.3.1、arraylist是线程安全的么,怎么让他线程安全

不是,

​ 1、使用synchronized关键字;

​ 2.使用Collections.synchronizedList();

1.3.2、Arraylist的扩容机制

在这里插入图片描述

1.3.3 Arraylist的源码分析

先上测试代码:

public static void main(String[] args) {
        ArrayList objects = new ArrayList<>();// 第一步
        objects.add("1号选手");  //第二步
        System.out.println(objects);
    }
//1、首先可以看到ArrayList实现了List接口;
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{   
    private static final long serialVersionUID = 8683452581122892189L;
//2、默认容量 DEFAULT_CAPACITY = 10
    private static final int DEFAULT_CAPACITY = 10;

    private static final Object[] EMPTY_ELEMENTDATA = {};
//3、一个空数组,用于实例化后进行复制
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//4、elementData为存储元素的数组
    transient Object[] elementData; // non-private to simplify nested class access
//5、实际数组长度
    private int size;
    
//当测试代码进入第一步时,实例化一个ArrayList时,就进入这样一个无参构造方法中,当然还有几个有参的,这里用的是无参的
public ArrayList() {
    //将elementData赋值为一个空数组,此时的Size为0
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
//当测试代码进入第二步时,进行add方法时
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;  //上一个语句到这一步中间隔了很多,到这里elementData的空间始终是够的,直接增加
        return true; 
    }
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
	}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //注意这个在第一次添加第一个元素时,add方法会进入到这里,显然第一次if为true,此时return 10,所以此时的数组实际长度瞬间变为10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
//add方法的最后操作就是进入扩展方法之中
private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); //相当于oldCapacity+(oldCapacity/2) 就是1.5倍
        if (newCapacity - minCapacity < 0)  //这里判断有没有必要扩容,当minCapacity大时,则会舍去前面的扩容操作
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)  //MAX_ARRAY_SIZE这个数很大,一般不会进入这里
            newCapacity = hugeCapacity(minCapacity);
       
        elementData = Arrays.copyOf(elementData, newCapacity);  //将elementData复制到新空间中
    }

1.4、LinkedList

1.4.1、LinkedList的使用

存储结构:双向链表

在这里插入图片描述

1.4.2、LinkedList的源码分析
//1、首先可以看到的是Linked List实现了List接口
public class LinkedList<E> extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
//链表的长度,或节点的个数
    transient int size = 0;
//头节点
    transient Node<E> first;
//尾节点
    transient Node<E> last;
//注意Linkedlist的数据结构是双向链表
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;
        }
    }
//当调用add方法时
public boolean add(E e) {
    linkLast(e);   //进入linkLast方法
    return true;
}
void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;       //如果是第一次加入,则设置其为头节点
    else
        l.next = newNode;  //否则在尾节点后面插入
    size++;
    modCount++;
}

1.5、ArrayList、Vector 和 LinkedList 有什么共同点与区别?

  • ArrayList、Vector 和 LinkedList 都是可伸缩的数组,即可以动态改变长度的数组。

  • ArrayList 和 Vector 都是基于存储元素的 Object[] array 来实现的,它们会在内存中开辟一块连续的空间来存储,支持下标、索引访问。但在涉及插入元素时可能需要移动容器中的元素,插入效率较低。当存储元素超过容器的初始化容量大小,ArrayList 与 Vector 均会进行扩容。

  • Vector 是线程安全的,其大部分方法是直接或间接同步的。ArrayList 不是线程安全的,其方法不具有同步性质。LinkedList 也不是线程安全的

  • LinkedList 采用双向列表实现,对数据索引需要从头开始遍历,因此随机访问效率较低,但在插入元素的时候不需要对数据进行移动,插入效率较高

  • ArrayList:基于动态数组,连续内存存储,适合下标访问(随机访问),**扩容机制(再补充详细些):**因为数组长度固定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数据,如果不是尾部插入数据还会涉及到元素的移动(往后复制一份,插入新元素),使用尾插法并指定初始容量可以极大提升性能、甚至超过linkedList(需要创建大量的node对象)

  • LinkedList:基于链表,可以存储在分散的内存中,适合做数据插入及删除操作,不适合查询:需要逐一遍历;遍历LinkedList必须使用iterator不能使用for循环,因为每次for循环体内通过get(i)取得某一元素时都需要对list重新进行遍历,性能消耗极大。

    另外不要试图使用indexOf等返回元素索引,并利用其进行遍历,使用indexOf对list进行了遍历,当结果为空时会遍历整个列表

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.6、如何实现数组和 List 之间的转换?

  • List转换成为数组:调用ArrayList的toArray方法。
  • 数组转换成为List:调用Arrays的asList方法。

1.7、Array 和 ArrayList 有何区别?

Array可以容纳基本类型和对象,而ArrayList只能容纳对象。
定义一个 Array 时,必须指定数组的数据类型及数组长度,即数组中存放的元素个数固定并且类型相同。

ArrayList 是动态数组,长度动态可变,会自动扩容。不使用泛型的时候,可以添加不同类型元素。

Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值