list 接口的特点是:有序,有下标,元素可重复。
它的实现类有几种 ArrayList 和 LinkedList,我们先看一下ArrayList的实现
public class TestArrayList {
public static void main(String[] args) {
List<String> arrayList = new ArrayList<String>();
arrayList.add("zhang3");
}
}
首先我们先new 了一个ArrayList<String>() 用一个引用arrayList来接受,执行的到的时候我们看一下源码
/**
* 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 = {};
原来直接创建了一个Object的空数组,长度为0,之后调用add方法的时候
/**
* 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;
}
这个方法干了两件事,第一行代码看它注释,我们可以知道重新创建一个数组,长度+1,第二行代码是给这个数组的 size+1 下标赋值 ,我们进去看一下
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
现在继续进入 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);
}
等追到这个方法,我们已经明白 其他就是创建了一个长度+1的数组,然后将数据copy过来,其实Arraylist的结构就是数组的结构,因为在数组结构中元素的存储空间是连续的因此通过下标很容易对元素进行定位,所以查询效率很好,相对应的插入和删除效率会低。
我在看一下linkedList这个实现类,LinkedList是链表结构,由多个节点组成,每个节点由3部分构成,元素,上一个节点对象,下一个节点对象,我们看一下源码
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
/**
* Constructs an empty list.
*/
public LinkedList() {
}
在LinkedList的成员变量中,有长度 size ,上一个节点对象 first, 下一个节点对象,这个类里面有个 静态成员内部类
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;
}
}
这是一个 节点的类,包含3个部分,元素,上个节点对象,下个节点对象。
List<String> linkedList = new LinkedList<String>();
linkedList.add("li4");
linkedList.add("wang5");
我们看一下代码,当new 出来一个LinkedList对象的时候,调用无参构造,初始化成员变量,size为0, first 和 last 只是进行声明,没有指向任何区域为 null
调用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)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
刚开始last 为null,l 引用也赋值为null,新建一个节点对象,调用它的有参构造,将 l 变量作为上个节点对象(prev)传入,下个节点对象(next)为null,将成员变量last赋值后,判断 如果是第一个节点,将上一个节点指向它自身,如果不是,将下一个节点指向它自身(注:这里l.next l指向的实际已是新创建节点 newNode的地址)。
如果查询的时候,只能从头或者 尾一个个查到我们需要的元素,get(i)这个方法做过优化,例如如果集合一共有5个元素,我们要查第二个 就会从头开始,如果要查第四个会从尾开始
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(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;
}
}
所以,这个链式结构,增删要快,查询要慢。
最后还有一个vector实现类,这个实现类也是数组结构,它是jdk1.0遗留下来的vector为了保证线程安全,采用了重量级的实现方式,在vector所有的方法都设计为同步方法,这样多线程在访问同一个vector对象的时候不会产生同步问题,但牺牲了效率。
例如:
public void add(E e) {
int i = cursor;
synchronized (Vector.this) {
checkForComodification();
Vector.this.add(i, e);
expectedModCount = modCount;
}
cursor = i + 1;
lastRet = -1;
}