提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
List集合
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、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);
}
可以看出上述代码的问题:更改元素和迭代元素是由不同的对象完成 (Iterator和list对象),这样会导致并发修改错误
集合中提供了fast-fail机制提供了上述的并发错误
LinkedList中的LinstIterator提供了add方法,这样修改容器元素由相同对象完成,不会抛出异常
总结
- List集合中包含ArrayList, LinkedList以及Vector;
- 对于Vector不提倡使用,同样使用数组保存元素,扩容方式为2倍,但是Vector是线程安全的,方式由Synchronize修饰,效率较低。
- ArrayList底层使用数组,默认容量10,扩容方式为1.5倍,使用空参构造采用懒加载方式; LinkedList底层使用双向链表,源码中优美点是node()方法,提高效率。
- 迭代器提供了遍历集合的方法。但是普通的Iterator不支持遍历过程中修改元素,原因在于操作集合元素对象不同;因此引入ListIterator,自带add方法