java 集合———List 实现类之 ArrayList、LinkedList

源码基于jdk 1.7.81

集合

Java 集合可分为 Collection 和 Map 两种体系,下面这是Collection 的继承树,我们可以看出 List 接口和 Set 接口继承自Collection 接口.

List 接口 : 存储有序的,可以重复的元素。

Set 接口:存储无序的,不可重复的元素,相当于我们高中学过的集合

Vector ArrayList LinkedList 是 List 的主要实现类。下面我们看一下ArrayList 和 LinkedList.

Collection 接口常用方法
boolean add(E e) 确保此 collection 包含指定的元素
boolean add(Collection<? extends E> c) 将指定 collection 中所有元素都添加到此 collection 中
void clear() 移除此 collection 中的所有元素
boolean contains(Object  o)

如果此 collection 包含所指定元素,则返回true

boolean equals(Object  o) 比较此collection 与指定对象是否相等
boolean remove(Object  o) 

从此 collection 中移除指定元素的单个实例

removeAll (Collection<?> c) 移除此 collection 中那些也包含在 collection 的元素
Object [ ] toArray() 返回包含此 collection 中所有元素的数组
扫描二维码关注公众号,回复: 2334847 查看本文章

ArrayList 

ArrayList 简介

ArrayList 本质上是一个动态数组,可以灵活的设置数组的大小。ArrayList并不是线程安全的,因此一般建议在单线程中使用ArrayList。

ArrayList 继承了 AbstractList 类,实现了List<E>, RandomAccess, Cloneable, java.io.Serializable 接口,

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ArrayList 实现了java.io.Serializable 接口,意味着ArrayList 支持序列化,能通过序列化去传输。

ArrayList 实现了Cloneable 接口,说明它重写了clone()方法。

ArrayList 的成员变量

从下面的源码来看,ArrayList本质上是数组来存储元素的,所以也使得ArrayList 具有数组的特性,具有随机存取的特点。


    private static final long serialVersionUID = 8683452581122892189L;

    private transient Object[] elementData;//这个数组用来存储元素。

    private int size;//size 表示当前长度。

header是双向链表的表头,它是双向链表节点所对应的类Entry的实例。Entry中包含成员变量: previous, next, element。其中,previous是该节点的上一个节点,next是该节点的下一个节点,element是该节点所包含的值。 size是双向链表中节点的个数。 

ArrayList 的构造方法。

    public ArrayList(int initialCapacity) {
	    super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
	    this.elementData = new Object[initialCapacity];
    }

    //默认构造函数
    public ArrayList() {
	this(10);//初始容量为10
    }
    
    public ArrayList(Collection<? extends E> c) {
	    elementData = c.toArray();//把 C 集合里面的元素传给elementData 数组。
	    size = elementData.length;
	    if (elementData.getClass() != Object[].class)
	        elementData = Arrays.copyOf(elementData, size, Object[].class);
    }    

ArrayList 有三个构造函数,从 ArrayList() 我们可以看出 集合默认情况下的初始容量为10

调用 ArrayList(int initialCapacity) 构造函数我们可以自定义数组的 初始容量。

ArrayList(Collection<? extends E> c) 构造函数是将容器数组化处理并将这个数组值赋给Object数组。

ArrayList 常用方法

1. 增加一个元素

​
/**
 * 增加一个元素	
 */
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  扩容
    elementData[size++] = e;
    return true;
}

​//给指定位置增加一个元素
public void add(int index, E element) {
	if (index > size || index < 0) //越界检查
	    throw new IndexOutOfBoundsException(
		"Index: "+index+", Size: "+size);

	ensureCapacity(size+1);  //判断扩容
	System.arraycopy(elementData, index, elementData, index + 1,
			 size - index);
	elementData[index] = element;
	size++;
    }

ArrayList 增加元素和数组增加元素一样,因为 ArrayList 是一个动态数组,所以它的容量也必须随着元素的增加而增加,ensureCapacityInternal(size + 1) 方法就是判断数组容量是否不够,如果数组满了即扩容。

2. 获得指定位置的元素 

/**
* 返回指定位置的元素
*/
public E get(int index) {
        rangeCheck(index);//合法性检查

        return elementData(index);
    }
private void RangeCheck(int index) {
	if (index >= size)
	    throw new IndexOutOfBoundsException(
		"Index: "+index+", Size: "+size);
    }

 ArrayList 可以通过数组的下标来随机的存取元素。当下标小于0或者大于数组的当前元素时抛出一个异常。

 3. 返回某元素在集合中第一次出现的位置。

/**
*判断某元素是否包含在集合中
*/
public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

public int indexOf(Object o) {
        if (o == null) {  //元素为null
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

判断元素是否包含在集合中是通过遍历数组来实现的。对于indexof方法做几点说明:ArrayList中可以存放null元素,indexof是返回elementData数组中值相同的首个元素的下标,indexof中比较方法是equals,而equals是比较元素的值,因此必须对null单独查找。如果未找到该元素则返回-1 。

4. 扩容

​
public void ensureCapacity(int minCapacity) {
	modCount++;
	int oldCapacity = elementData.length;
	if (minCapacity > oldCapacity) {
	    Object oldData[] = elementData;
	    int newCapacity = (oldCapacity * 3)/2 + 1; //新的容量
    	    if (newCapacity < minCapacity)
		    newCapacity = minCapacity;     
            //返回一个新的数组,容量为 newCapacity
            elementData = Arrays.copyOf(elementData, newCapacity);
	 }
}

​

ArrayList 中扩容是元来的 3/2+1 倍,通过Arrays. copyOf()方法返回一个新的数组来实现的。

LinkedList

LinkedList 简介

LinkedList 继承了AbstractSequentialList 的双向链表,实现了List<E>, Deque<E>, Cloneable, java.io.Serializable 接口。

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

LinkedList 继承了Cloneable 接口,重写了clone()方法,可以被克隆。

LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。

LinkedList 的成员变量

LinkedList 本质上是用一个双向链表来存储元素的,它比较适合于频繁的插入或者删除元素。


private transient Entry<E> header = new Entry<E>(null, null, null);//头结点
private transient int size = 0;//当前元素个
    
private static class Entry<E> {
	E element;
	Entry<E> next;
	Entry<E> previous;

	Entry(E element, Entry<E> next, Entry<E> previous) {
	    this.element = element;//元素
	    this.next = next;//后继
	    this.previous = previous;//前驱
	}
}

LinkedList 构造函数 

//默认构造函数
    public LinkedList() {
        header.next = header.previous = header;
    }
	//保存Collection中的全部元素
    public LinkedList(Collection<? extends E> c) {
	this();
	addAll(c);
    }

LinkedList 有两个构造函数,默认构造函数LinkedList() 使得header 节点自己构成了一个双向链表。 LinkedList(Collection<? extends E> c) 保存了Collection 接口中所有的元素。

LinkedList 新增方法

1. 增加一个元素在第一位

    ​//在第一位增加一个元素
    public void addFirst(E e) {
	    addBefore(e, header.next);
    }
    private Entry<E> addBefore(E e, Entry<E> entry) {
    	//一个新的节点,后继是头结点的后继,前驱是头结点
		Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);

		newEntry.previous.next = newEntry;
		
		newEntry.next.previous = newEntry;
		size++;
		modCount++;
		return newEntry;
    }
    

 

2. 增加一个元素在最后一位 

/**
* 增加元素在最后一位
*/
public void addLast(E e) {
        linkLast(e);
    }
void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);//一个新的节点,它的前驱是last
        last = newNode; //last 指向新的节点
        if (l == null) //当前集合为空 
            first = newNode;//这个新结点就是头结点
        else
            l.next = newNode;//集合不为空,把last 放在最后。
        size++;
        modCount++;
    }

3. 获取最后一个元素 

/**
* 获取最后一位元素
*/
 public E getLast()  {
    if (size==0)
	    throw new NoSuchElementException();
	return header.previous.element;//头结点的前驱
 }

4. 获取第一位元素 

/**
* 获取第一位元素
*/
public E getFirst() {
	if (size==0)
	    throw new NoSuchElementException();

	return header.next.element;//头结点的后继
    }

5. 删除第一个元素

/**
* 删除第一个元素
*/
 public E removeFirst() {
	return remove(header.next);//删除头结点的后继
 }

 6. 删除最后一个元素

/**
* 删除最后一个元素
*/
public E removeLast() {
	return remove(header.previous);//删除头结点的前驱
}

因为 LinkedList 是用链表实现的所以不用考虑扩容的问题。 

底层使用双向链表实现的,所有 LinkedList 拥有链表的所有特点。基于LinkedList 的特点,LinkedList 适合于频繁的插入删除.

利用LinkedList 的基本操作

LinkedList 可以作为先进先出的队列

LinkedList 可以作为先进后出的栈

LinkedList 的遍历方式

1.通过迭代器来遍历

for(Iterator iter = list.iterator(); iter.hasNext();)
    iter.next();

2.通过快速访问来遍历

int size = list.size();
for (int i=0; i<size; i++) {
    list.get(i);        
}

3. 通过 for 循环来遍历

for (Integer integ:list) 
    ;

猜你喜欢

转载自blog.csdn.net/alyson_jm/article/details/80703944