List接口——Collection子接口
1. List接口
1)概述
- 存储一个一个的数据
- 有序
2)常用的方法
List是Collection的子接口,Collection中声明的方法,在List的实现类中都可以使用。由于List是有序的,所有额外添加了一些方法。
- void add(int index, Object ele): 在index位置插入ele元素
- boolean addAll(int index, Collection eles): 从index位置开始将eles中的所有元素添加进来
- Object get(int index): 获取指定index位置的元素
- int indexOf(Object obj): 返回obj在集合中首次出现的位置
- int lastIndexOf(Object obj): 返回obj在当前集合中末次出现的位置
- Object remove(int index): 移除指定index位置的元素,并返回此元素
- Object set(int index, Object ele): 设置指定index位置的元素为ele
- List subList(int fromIndex, int toIndex): 返回从fromIndex到toIndex位置的子集合
3)总结常用方法(必须掌握的)
- 增: add(Object obj)
- 删: remove(Object obj)/remove(int index)
- 改: set(int index, Object ele)
- 查: get(int index)
- 插: add(int index, Object ele)
- 长度: size()
- 遍历: iterator() / 增强for
4)代码举例
List list = new ArrayList();
list.add(123);//自动装箱
list.add(567);
list.add("AA");
list.remove("AA");
list.remove(new Integer(123));
list.set(1, "BB");
list.add(1, "CC");
System.out.println(list);
System.out.println(list.get(2));
System.out.println(list.size());
List遍历
@Test
public void test2(){
List list = new ArrayList();
list.add(123);//自动装箱
list.add(567);
list.add("AA");
//方式一:使用迭代器
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//方式二:增强for
// for(Object obj : list){
// System.out.println(obj);
// }
//方式三:一般for循环
// for(int i = 0;i < list.size();i++){
// System.out.println(list.get(i));
// }
}
@Test
public void test3(){
List list = new ArrayList();
list.add(123);//自动装箱
list.add(567);
list.add("AA");
List list1 = Arrays.asList(1,2,3);//数组转换为List
list.addAll(1, list1);
System.out.println(list);
List list2 = list.subList(1,3);//List截取,左闭右开
System.out.println(list2);
}
2. List不同的实现类
1) ArrayList(最常用的)
- List的主要实现类;
- 线程不安全的,效率高;
- 底层使用Object[]存储
2) LinkedList
- 底层使用双向链表存储数据;
- 对于频繁的插入、删除操作,使用此类效率高。
3) Vector
List的古老实现类;
线程安全的,效率低;
底层使用Object[]存储
4) 扩展:数据结构中的数据存储结构
- 真实数据存储结构:
顺序表(一维数组)、链表 - 抽象数据存储结构
栈、队列、树、图
3. List的源码分析
1) jdk7版本源码
- 构造器:初始化底层的elementDate的Object[]数组,长度为10.
ArrayList list = new ArrayList();
transient Object[] elementData; // non-private to simplify nested class access//list底层的对象数组
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this(10);
}
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
- 首次add():elementDate[0] = new Integer(123);
list.add(123);
当遇到底层数组容量达到零界点时,调用add(“AA”),一旦添加的数据的个数超出了底层数组的长度,需要考虑扩容。
默认容量扩容扩容为原来的1.5倍,同时将旧数组中的数据都复制到新的数组中。
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;//list中包含的元素的个数
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!//确认是否需要扩容
elementData[size++] = e;//执行到这里说明elementData的容量够用。
return true;
}
确认是否需要扩容
private void ensureCapacityInternal(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//如果minCapacity大于elementData.length,说明此此添加list的容量已经不够,需要扩容
grow(minCapacity); //扩容
}
扩容
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//默认扩容比例:1.5倍
//特殊情况的扩容比例:
if (newCapacity - minCapacity < 0)
//如果扩容1.5倍后依然不够,则直接扩容至需要的容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//如果扩容至需要的容量后,已经超过数组的最大限制
newCapacity = hugeCapacity(minCapacity);
//创建新长度的数组,并将原有数组中的数据copy到新的数组中。
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
2)jdk8版本源码分析
- 构造器:初始化底层elementData的Object[]数组为{}
ArrayList list = new ArrayList();
//懒加载:
//使用空参构造器,初始化为长度为0的object数组。
//当有任何操作后,长度更改为DEFAULT_CAPACITY=10。
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
list.add(123);
- 首次add(): 此时底层创建长度为10的elementData数组,并将new Integer(123)存放到角标0的索引位置。
- 当底层数组容量达到零界点时,此时调用add(“AA”),一旦添加的数据的个数超出了底层数组的长度,就需要考虑扩容 。
list.add("AA");
默认容量扩容扩容为原来的1.5倍,同时将旧数组中的数据都复制到新的数组中。
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
初始化底层数组
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;//默认初始化list容量
private void ensureCapacityInternal(int minCapacity) {
//如果是首次调用add(),则此时的minCapacity赋值为10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//==,说明是第一次添加数据
//第一次添加数据,需要初始化list容量,取默认值和第一次添加需要容量的最大值,也就是10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//判断是否需要扩容
ensureExplicitCapacity(minCapacity);
}
判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
//首次添加数据,minCapacity(10)大于实际数组长度elementData.length(0)
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
扩容:扩容为原来的1.5倍
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//默认扩容为原来的1.5倍
//特殊情况下的扩容方案:
if (newCapacity - minCapacity < 0)
//扩容后依然不够需要的容量,就直接扩容至需要的容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//扩容后的容量超过了最大容量限制,就设置为int型最大值或最大容量
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
3)说明
jdk7中ArrayList底层的数组的创建类似于单例模式中的饿汉式
jdk8中ArrayList底层的数组的创建类似于单例模式中的懒汉式
6. Vector源码分析
1) 构造器
Vector v = new Vector();
public Vector() {
this(10);//默认初始容量为10
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
/**
* The amount by which the capacity of the vector is automatically
* incremented when its size becomes greater than its capacity. If
* the capacity increment is less than or equal to zero, the capacity
* of the vector is doubled each time it needs to grow.
*
* @serial
*/
//需要扩容时,Vector自动扩容的量。
//如果扩容增量小于或等于0,则每次需要增长时,向量的容量将增加一倍。
protected int capacityIncrement;
2)add()
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
判断是否需要扩容
/**
* This implements the unsynchronized semantics of ensureCapacity.
* Synchronized methods in this class can internally call this
* method for ensuring capacity without incurring the cost of an
* extra synchronization.
*
* @see #ensureCapacity(int)
*/
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
扩容
- jdk7和jdk8中在创建对象时,底层的操作相同,都是创建默认长度为10的Object[]。
- 当底层容量不足时,默认扩容为原来的2倍,然后将之前的数组元素,复制到新的数组中
- 也可以在创建时,在构造器中显式指明扩容是自动扩容的增量。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
7. LinkedList源码分析
1)构造器
LinkedList list = new LinkedList();
/**
* Constructs an empty list.
*/
public LinkedList() {
}
2)链表节点内部类
- LinkedList在添加数据时,元素封装在Node对象中,并指明其前一个和后一个元素。
- 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;//前向指针的产生
}
}
transient Node<E> first;
transient Node<E> last;
2) add()
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)//返回true,表示是首次添加
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
3) addFirst()/addLast()
添加元素到链表的第一个/最后一个位置。
public void addFirst(E e) {
linkFirst(e);
}
/**
* Links e as first element.
*/
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
public void addLast(E e) {
linkLast(e);
}
/**
* Links e as last element.
*/
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++;
}
4) getFirst()/getLast()
获取链表第一个/最后一个位置元素。
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
5) removeFirst()/removeLast()
删除并返回第一个/最后一个元素。如果没有该元素,则抛出异常。
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
6) pollFirst()/pollLast()
删除并返回第一个/最后一个元素。如果没有该元素,则返回null。
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
8. 小结
1) 建议开发中,如果基本确定底层数组的容量,建议使用带参数的构造器,避免底层不断的扩容和复制操作
ArrayList list = new ArrayList(int initialCapacity);//new Object[initialCapacity];
2) 对于数组来说,查找操作的复杂度是O(1),插入或删除操作的复杂度是O(n)
对于链表来说,查找操作的复杂度是O(n),插入或删除操作的复杂度是O(1)
3) 开发中,如果很少执行插入或删除操作,建议使用ArrayList
如果频繁的使用插入或删除操作,建议使用LinkedList