ArrayList和LinkedList源码初看

本文详细探讨了ArrayList和LinkedList两种Java集合类的区别。ArrayList基于数组实现,查询快但增删慢,扩容机制为原长度的1.5倍。LinkedList采用双向链表结构,适合频繁的增删操作但查询慢。同时,文章通过源码分析了它们的索引获取和元素操作的效率差异。
摘要由CSDN通过智能技术生成

无论是是ArrayList还是LinkedList,都是List的子类,因此它就自然而然的继承了List的特点;

特点:

  • 有序排列,存取的规律为先进先出,如存入的顺序为[1,2,3,4],那么取出时的顺序也为[1,2,3,4
  • 允许重复元素,与Set集合相反
  • 自带索引,通过索引就可以操作集合中的每个元素。

ArrayList的存储的结构是数组结构。内部的实现原理能够让其动态增长和缩减

正因为是动态的数组结构,因此ArrayList元素增删慢,查找快

ArrayList底层源码是基于数组来实现的,并且默认初始的容量为10

   transient Object[] elementData; // non-private to simplify nested class access
   private int size;
	/**
     * Default initial capacity.
	*/
    private static final int DEFAULT_CAPACITY = 10;

get(int index)方法:

   public E get(int index) {
        rangeCheck(index); //检查数组是否越界
        return elementData(index);//直接返回数组的下标
    }

add(E e) 方法

   public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 判断是否需要扩容
        elementData[size++] = e; //执行添加操作
        return true;
    }

此时,add方法中调用了ensureCapacityInternal(size + 1); ,继续翻阅该方法

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); //此处嵌套了两个方法
    }

此时又继续调用方法ensureExplicitCapacity方法,且内部执行了calculateCapacity用于扩容

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

    // overflow-conscious code
    if (minCapacity - elementData.length > 0) //判断是否需要执行扩容,如果该长度大于数组的长度,就执行扩容,且每次扩容按1.5进行
        grow(minCapacity);
}
 private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

最终调用了grow方法

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 oldCapacity = elementData.length;
  • int newCapacity = oldCapacity + (oldCapacity >> 1); //

oldCapacity >> 1)执行了右移操作,相当于 oldCapacity / 2

这里就是扩容大小确定的地方,相当于新的最大容量是 旧的数组长度+旧数组的0.5长度

LinkedList是一个双向链表,因此除了头结点外的每一个节点都有前驱指针和后继指针

正因为LinkedList的存储结构为链式存储,因此具备了链表的特点:

  1. LinkedList的查询速度慢
  2. LinkedList的增加和删除操作速度快

当我们需要对LinkedList进行遍历操作时,可以使用迭代器完成。学过数据结构这门课,就常因为那绕来绕去的前后指针而留下坏印象,但是java当中提供了丰富的类库

//new一个String类型的LinkedList,插入三个元素
LinkedList<String> linkedList = new LinkedList<>();
        linkedList.add("1");
        linkedList.add("2");
        linkedList.add("3");
Iterator<String> iterator = linkedList.iterator();
//使用迭代器的next方法
 String next = iterator.next();
 System.out.println(next);

linkedList.add()方法是在链表的尾部进行插入,当我们需要对链表的中间位置进行增删操作时,可以使用迭代器listIterator,这是Iteraror的子接口。

//访问链表的下一节点,并在下一节点处插入字符“123”
ListIterator<String> it = linkedList.listIterator();
        String next = it.next();
        it.add("123");
        System.out.println(next);
        System.out.println(linkedList);

linkedList也有通过所谓的索引来获取链表的值:

 LinkedList<String> linkedList = new LinkedList<>();
        linkedList.add("1");
        linkedList.add("2");
        linkedList.add("3");
        linkedList.add("4");
		//根据索引值获取 "2"
        String s = linkedList.get(1);
        System.out.println(s); //结果为2

但可以通过翻阅源码查看LinkedList的get方法:

  public E get(int index) {
        checkElementIndex(index);//检查是否越界
        return node(index).item;
  }

可以看到此处调用了一个node方法去查找该位置的Element,继续查看node()的源码:

Node<E> node(int index) {
    if (index < (size >> 1)) {// >>将链表的长度size右移1一位,即判断index跟size/2的大小
        Node<E> x = first; 
        for (int i = 0; i < index; i++) //从头开始遍历
            x = x.next;
        return x;
    } else {//从链表的尾部开始遍历
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

可以看到,这种从索引位置获取的元素的方式,是通过循环的方式来获得,对比ArrayList的索引获取,效率非常的低。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值