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

源码基于jdk 1.7.81

集合

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

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

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

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

 

java 集合———List 实现类之 ArrayList、LinkedList_第1张图片

 

Collection 接口常用方法
boolean add(E e) 确保此 collection 包含指定的元素
boolean add(Collection 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 中所有元素的数组

 

ArrayList 

ArrayList 简介

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

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

public class ArrayList extends AbstractList
        implements List, 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 表示当前长度。

 

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 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 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, Deque, Cloneable, java.io.Serializable 接口。

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

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

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

LinkedList 的成员变量

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


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

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

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

LinkedList 构造函数 

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

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

LinkedList 新增方法

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

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

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

 java 集合———List 实现类之 ArrayList、LinkedList_第2张图片

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

 

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

java 集合———List 实现类之 ArrayList、LinkedList_第3张图片

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

3. 通过 for 循环来遍历

for (Integer integ:list) 
    ;

 

 

你可能感兴趣的:(java基础,java)