List集合小结

本文深入探讨了Java中的List集合,重点分析了ArrayList和LinkedList的实现原理。ArrayList基于数组实现,初始容量为10,扩容采用1.5倍策略。LinkedList则采用双向链表,其get方法优化了查找效率。此外,文章还讨论了迭代器Iterator和ListIterator的区别,强调了ListIterator在遍历和修改集合时的优势。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、List集合

public interface List<E> extends Collection<E>
public interface Collection<E> extends Iterable<E>

顺序结构,集合元素有序可重复,有ArrayList, LinkedList, Vector的实现类
常用方法:

add(), addAll(), remove(), isEmpty(), size(), get()等

在代码中Collection实现了Iterable接口,说明List是一种可迭代对象

二、ArrayList

2.1 概述

List的实现类,底层使用数组进行实现;顺序存储
了解:扩容方式,底层数据结构

List接口中常用方法:
增加:add(int index, E element)
删除:remove(int index) remove(Object o)
修改:set(int index, E element)
查看:get(int index)
判断:

2.2 源码

2.2.1 JDK1.8 版本

采用懒加载的方式,在第一次add的时候才进行初始化

transient Object[] elementData;
private int size;  // 数组元素个数

JDK1.8中,无参构造

public ArrayList() {
  // 首先通过默认的空数组 {}进行赋值,在第一次add的时候初始化
  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public boolean add(E e) {
	ensureCapacityInternal(size + 1);  // 检查是否需要扩容
	elementData[size++] = e;  // 设置元素
	return true;
}

检查过程中,确定当前的容量:

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

如果 数组是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,说明是 {}数组,因此分配默认值 10

真正分配长度以及扩容在grow方法中

// 进入方法的前提
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);
}

2.2.2 ArrayList小结

初始容量10, 扩容时进行1.5倍扩容;采用懒加载的方式,当使用无参构造的时候传递 {} 数组,当第一次进行 add 的时候在grow()方法中分配空间。

三、LinkedList

底层使用双向链表,在模拟链表的时候注意几个成员变量:头结点first, 尾结点last,注意边界就行;
提供常用方法,支持在链表头/尾进行元素的删除和增加

源码中比较优美的地方:get元素的时候,获取node结点,间接调用了node()方法

Node<E> node(int index) {
    if (index < (size >> 1)) {
        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;
    }
}

在获取元素的时候,传统都是从头结点依次遍历;而在源码中,他首先判断下标是否超过数组一半,超过则从尾部进行遍历,提高效率。

四、迭代器

4.1 Iterator, iterator(), Iterable的区别?

对于集合中的接口,都间接继承了 Iterable接口

public interface Collection<E> extends Iterable<E>

该接口中提供了迭代方法:iterator(), 返回Iterator的接口类型对象

Iterator<E> iterator();

在Iterator接口类型中提供了hasNext() 和 next() 方法

4.2 ListIterator的作用?

使用传统的Iterator接口类型,提供了remove()方法,在迭代数组的时候可以删除元素;但是在添加元素的时候只能通过集合中的add()

ArrayList<Integer>list = new ArrayList<>();
list.add(1);
for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext();iterator.next()) {
    list.add(14);
}

可以看出上述代码的问题:更改元素和迭代元素是由不同的对象完成 (Iteratorlist对象),这样会导致并发修改错误
在这里插入图片描述
集合中提供了fast-fail机制提供了上述的并发错误

LinkedList中的LinstIterator提供了add方法,这样修改容器元素由相同对象完成,不会抛出异常

总结

  1. List集合中包含ArrayList, LinkedList以及Vector;
  2. 对于Vector不提倡使用,同样使用数组保存元素,扩容方式为2倍,但是Vector是线程安全的,方式由Synchronize修饰,效率较低。
  3. ArrayList底层使用数组,默认容量10,扩容方式为1.5倍,使用空参构造采用懒加载方式; LinkedList底层使用双向链表,源码中优美点是node()方法,提高效率。
  4. 迭代器提供了遍历集合的方法。但是普通的Iterator不支持遍历过程中修改元素,原因在于操作集合元素对象不同;因此引入ListIterator,自带add方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值