您的位置:首页 > 编程语言 > Java开发

Java容器源码解析

2016-07-25 14:47 633 查看

Java容器概览



迭代器

Iterable<T>接口

/**实现该接口的对象(这里只讨论容器),可以用foreach表达式进行迭代
* Implementing this interface allows an object to be the target of the "foreach" statement.
*/
public interface Iterable<T> {
/**返回容器的迭代器(Iterator)
* Returns an iterator over a set of elements of type T.*/
Iterator<T> iterator();
}

示例代码:

public static void main(String[] args){
List<Integer> list=new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
for(Integer i:list){//foreach语句迭代
System.out.print(i+" ");
}
}

输出:

1 2 3 4 5

Iterator<E>接口

/**容器的单向迭代器
* An iterator over a collection.  */
public interface Iterator<E> {
/**如果存在下一个元素,返回true
* Returns {@code true} if the iteration has more elements. */
boolean hasNext();
/**返回下一个元素
* Returns the next element in the iteration. */
E next();
/**移除最近返回的那个元素(可选操作)。注意:
1、每次remove调用前必须有一个与之对应的next操作(暗含意思是:不可能连续出现两次remove操作,也不能还没执行next操作就直接执行remove操作)。
2、在迭代过程中,如果除了当前迭代器的remove对容器产生的结构性修改外,还存在别的方式对容器产生了结构性修改,那么迭代器的行为将不可预见。
3、基于第二条,在多线程环境下,迭代行为也是不可预见的。
* Removes from the underlying collection the last element returned by this iterator (optional operation).
* This method can be called only once per call to {@link #next}.  The behavior of an iterator is unspecified
* if the underlying collection is modified while the iteration is in progress in any way
* other than  by calling this method.*/
void remove();
}


单向迭代器的示意图:



ArrayList部分源码
public class ArrayList<E> extends AbstractList<E> implements List<E>,RandomAccess, Cloneable, java.io.Serializable {
/**线性表结构性修改的次数,所谓结构性修改就是改变了线性表长度的修改。
* The number of times this list has been <i>structurally modified</i>.
Structural modifications are those that change the size of the list.*/
protected transient int modCount = 0;//在AbstractList<E>类中
/**ArrayList中存放元素的缓冲数组,因为ArrayList是动态扩展的,因此该缓冲数组的length大于等于实际元素的个数
* The array buffer into which the elements of the ArrayList are stored.
The capacity of the ArrayList is the length of this array buffer.*/
private transient Object[] elementData;
/**ArrayList中实际元素的个数
* The size of the ArrayList (the number of elements it contains).*/
private int size;
//Iterable接口的实现,默认返回单项迭代器
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor;       // index of next element to return 内部游标
int lastRet = -1; // index of last element returned; -1 if no such 最后返回元素的索引下标
int expectedModCount = modCount;//记录开始迭代时,结构性修改的次数

public boolean hasNext() {
return cursor != size;
}

@SuppressWarnings("unchecked")
public E next() {
checkForComodification();//每次调用next前先检查是否有结构性的修改
int i = cursor;
if (i >= size)
throw new NoSuchElementException();//游标超出索引范围,将会报该异常,因此每次调用next前应该先判定hasNext
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];//更新最后返回元素的索引下标
}

public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();

try {
ArrayList.this.remove(lastRet);
//查看源码可以看到,该操作会增加结构性修改的次数,如果在该迭代器之外这个方法被调用,该迭代过程会报异常。
cursor = lastRet;
lastRet = -1;//移除元素之后,最后返回元素的索引下标置为无效。这就是为什么要求每次remove调用必须有一个与之对应的next操作。
expectedModCount = modCount;//当前迭代器remove操作之后会更新结构性修改的次数
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
//如果在迭代过程中,除了当前迭代器的remove操作,还有别的操作对容器产生结构性修改,报异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}


Iterator示例代码:
public class IteratorTest {
public static void main(String[] args){
ArrayList<Integer> arrayList=new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7));
Iterator<Integer> iterator=arrayList.iterator();
//标准的迭代方式
while(iterator.hasNext()){//Iterator迭代
Integer i =iterator.next();
System.out.print(i+" ");
}
System.out.println();
//没有检查hasNext就直接调用next产生的异常
try{
Integer i =iterator.next();
}catch(NoSuchElementException e){
e.printStackTrace();
}
//正确的remove
iterator=arrayList.iterator();
if(iterator.hasNext()){
iterator.next();
iterator.remove();
}
System.out.println("正确移除以后:");
for(Integer i:arrayList){//remove之后
System.out.print(i+" ");
}
System.out.println();
//错误的移除1:没有执行next就直接执行remove产生的异常
try{
//每次获取的迭代器都是新的迭代器
iterator=arrayList.iterator();
if(iterator.hasNext()){//错误的remove
iterator.remove();
}
}catch(IllegalStateException e){
e.printStackTrace();
}
//错误的移除2:一次next操作,连续两次remove操作
try{
//每次获取的迭代器都是新的迭代器
iterator=arrayList.iterator();
if(iterator.hasNext()){//错误的remove
iterator.next();
iterator.remove();
iterator.remove();
}
}catch(IllegalStateException e){
e.printStackTrace();
}
System.out.println("错误移除2以后:");
for(Integer i:arrayList){//remove之后
System.out.print(i+" ");
}
System.out.println();
//错误的移除3:多个迭代器交叉执行带来的混乱产生的异常
Iterator<Integer> iterator1=arrayList.iterator();
Iterator<Integer> iterator2=arrayList.iterator();
try{
if(iterator1.hasNext()){
if(iterator2.hasNext()){
iterator2.next();
iterator2.remove();
}
Integer i=iterator1.next();
}
} catch(ConcurrentModificationException e){
e.printStackTrace();
}
}
}


输出:

1 2 3 4 5 6 7

java.util.NoSuchElementException

at java.util.ArrayList$Itr.next(Unknown Source)

at javabase.collection.IteratorTest.main(IteratorTest.java:17)

正确移除以后:

2 3 4 5 6 7

java.lang.IllegalStateException

at java.util.ArrayList$Itr.remove(Unknown Source)

at javabase.collection.IteratorTest.main(IteratorTest.java:37)

java.lang.IllegalStateException

at java.util.ArrayList$Itr.remove(Unknown Source)

at javabase.collection.IteratorTest.main(IteratorTest.java:49)

错误移除2以后:

3 4 5 6 7

java.util.ConcurrentModificationException

at java.util.ArrayList$Itr.checkForComodification(Unknown Source)

at java.util.ArrayList$Itr.next(Unknown Source)

at javabase.collection.IteratorTest.main(IteratorTest.java:68)

List<E>接口

public interface List<E> extends Collection<E> {
ListIterator<E> listIterator();
......
}
实现List<E>接口的类,将具有双向迭代的能力。其listIterator方法返回ListIterator<E>接口的具体实现。

ListIterator<E>接口

/**双向迭代器,迭代器可以双向移动,并在迭代过程中允许修改链表(删除或设值),并且可以获取迭代器当前的位置。
* An iterator for lists that allows the programmer to traverse the list in either direction, modify
* the list during iteration, and obtain the iterator's current position in the list.  */
public interface ListIterator<E> extends Iterator<E> {
// Query Operations

/**参照单向迭代器*/
boolean hasNext();
E next();

/**如果存在前一个元素,返回true
* Returns {@code true} if this list iterator has more elements when
* traversing the list in the reverse direction.  */
boolean hasPrevious();

/**返回前一个元素
* Returns the previous element in the list and moves the cursor
* position backwards. */
E previous();

/**下一个元素的索引下标
* Returns the index of the element that would be returned by a
* subsequent call to {@link #next}. (Returns list size if the list
* iterator is at the end of the list.)*/
int nextIndex();

/**前一个元素的索引下标
* Returns the index of the element that would be returned by a
* subsequent call to {@link #previous}. (Returns -1 if the list
* iterator is at the beginning of the list.) */
int previousIndex();

// Modification Operations

/**移除最近返回的那个元素。
1、每次调用remove都必须有一次相应的next调用或previous调用。
2、在1的基础上,在remove与(next/previous)之间,不能有add操作。
* Removes from the list the last element that was returned by {@link
* #next} or {@link #previous} (optional operation).  This call can
* only be made once per call to {@code next} or {@code previous}.
* It can be made only if {@link #add} has not been
* called after the last call to {@code next} or {@code previous}.*/
void remove();

/**设置最近返回的那个元素的值。
* Replaces the last element returned by {@link #next} or
* {@link #previous} with the specified element (optional operation).
* This call can be made only if neither {@link #remove} nor {@link
* #add} have been called after the last call to {@code next} or
* {@code previous}. */
void set(E e);

/**将元素插入到线性表中,插入后迭代器位于新插入元素之后。
* Inserts the specified element into the list (optional operation).
* The element is inserted immediately before the element that
* would be returned by {@link #next}, if any, and after the element
* that would be returned by {@link #previous}, if any.  */
void add(E e);
}

双向迭代器示意图



ArrayList的部分源码
public class ArrayList<E> extends AbstractList<E> implements List<E>,RandomAccess, Cloneable, java.io.Serializable {
public ListIterator<E> listIterator() {
return new ListItr(0);
}
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}

public boolean hasPrevious() {
return cursor != 0;
}

public int nextIndex() {
return cursor;
}

public int previousIndex() {
return cursor - 1;
}

@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];//更新最后返回元素的索引下标
}

public void set(E e) {
if (lastRet < 0)//这里,set前必须有next/previous操作
throw new IllegalStateException();
checkForComodification();

try {
ArrayList.this.set(lastRet, e);//设置的是最后返回元素的值,这取决于最后一次执行的是next还是previous
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

public void add(E e) {
checkForComodification();

try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;//这里可以看出,为什么remove和(next/previous)之间不能有add操作,因为将上次返回元素的索引下标置为无效
//同理,set和(next/previous)之间也不能有add操作</span>
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}

......
}


ListIterator迭代示例代码:
public class ListIteratorTest {
public static void main(String[] args){
ArrayList<Integer> arrayList=new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7));
System.out.println("利用双向迭代器正向迭代:");
ListIterator<Integer> iteratorAhead=arrayList.listIterator();
while(iteratorAhead.hasNext()){
System.out.print(iteratorAhead.next()+" ");
}
System.out.println();
System.out.println("反向迭代的误解:");
//初试状态,迭代器在最前端
ListIterator<Integer> iteratorBackMisunderstand=arrayList.listIterator();
while(iteratorBackMisunderstand.hasPrevious()){
System.out.print(iteratorBackMisunderstand.previous()+" ");
}
System.out.println();
System.out.println("正确的反向迭代:");
//首先让迭代器指向最末端(正向迭代完成后所处在的位置)
ListIterator<Integer> iteratorBack=iteratorAhead;
while(iteratorBack.hasPrevious()){
System.out.print(iteratorAhead.previous()+" ");
}
System.out.println();
//魔鬼的步伐,先右边两步再左边一步
System.out.println("魔鬼的步伐:");
ListIterator<Integer> iteratorDisorder=arrayList.listIterator();
while(true){
if(iteratorDisorder.hasNext()){
System.out.print(iteratorDisorder.next()+" ");
}else{
break;
}
if(iteratorDisorder.hasNext()){
System.out.print(iteratorDisorder.next()+" ");
}else{
break;
}
if(iteratorDisorder.hasPrevious()){
System.out.print(iteratorDisorder.previous()+" ");
}else{
break;
}
}
System.out.println();
}
}
输出:
利用双向迭代器正向迭代:

1 2 3 4 5 6 7

反向迭代的误解:

正确的反向迭代:

7 6 5 4 3 2 1

魔鬼的步伐:

1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7

ListIterator修改示例代码
public class ListIteratorModificationTest {

public static void main(String[] args){
ArrayList<Integer> arrayList=new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7));
//正确设值:迭代过程中设置值,偶数位置的元素*2
ListIterator<Integer> iteratorSet=arrayList.listIterator();
while(iteratorSet.hasNext()){
//获取迭代器所处的位置
if(iteratorSet.nextIndex()%2==1){
Integer next=iteratorSet.next();
iteratorSet.set(2*next);
}else{
iteratorSet.next();
}
}
System.out.println("设值之后:");
for(Integer i:arrayList){
System.out.print(i+" ");
}
System.out.println();
//错误设值1:还没有执行next或previous操作就直接set,最后返回元素的索引下标还是无效的
ListIterator<Integer> iteratorSetWithoutNext=arrayList.listIterator();
try{
if(iteratorSetWithoutNext.hasNext()){
iteratorSetWithoutNext.set(100);
}
}catch(IllegalStateException e){
e.printStackTrace();
}
//错误设值2:set操作与next或previous操作中有add操作
ListIterator<Integer> iteratorSetAfterAdd=arrayList.listIterator();
try{
if(iteratorSetAfterAdd.hasNext()){
iteratorSetAfterAdd.next();
iteratorSetAfterAdd.add(1000);//插入元素
iteratorSetAfterAdd.set(100);
}
}catch(IllegalStateException e){
e.printStackTrace();
}
//正确的移除元素
System.out.println("移除之前:");
for(Integer i:arrayList){
System.out.print(i+" ");
}
System.out.println();
ListIterator<Integer> iteratorRemove=arrayList.listIterator();
if(iteratorRemove.hasNext())iteratorRemove.next();
if(iteratorRemove.hasNext())iteratorRemove.next();
if(iteratorRemove.hasPrevious())iteratorRemove.previous();
iteratorRemove.remove();
System.out.println("移除之后:");
for(Integer i:arrayList){
System.out.print(i+" ");
}
System.out.println();
//ArrayList的双向迭代器继承自其单向迭代器,remove操作就继承自其单向迭代器
//错误的移除1:没有执行next或previous就直接执行remove操作,参看单向迭代器
//错误的移除2:在next/previous与remove操作之间插入了add操作
ListIterator<Integer> iteratorRemoveAfterAdd=arrayList.listIterator();
try{
if(iteratorRemoveAfterAdd.hasNext()){
iteratorRemoveAfterAdd.next();
iteratorRemoveAfterAdd.add(1000);//插入元素
iteratorRemoveAfterAdd.remove();
}
}catch(IllegalStateException e){
e.printStackTrace();
}
//错误的移除3:一次next之后,连续两次remove操作,参看单向迭代器
//错误的移除4:迭代器交叉执行,参看单向迭代器
}
}
输出:
设值之后:

1 4 3 8 5 12 7

java.lang.IllegalStateException

at java.util.ArrayList$ListItr.set(Unknown Source)

at javabase.collection.ListIteratorModificationTest.main(ListIteratorModificationTest.java:31)

java.lang.IllegalStateException

at java.util.ArrayList$ListItr.set(Unknown Source)

at javabase.collection.ListIteratorModificationTest.main(ListIteratorModificationTest.java:42)

移除之前:

1 1000 4 3 8 5 12 7

移除之后:

1 4 3 8 5 12 7

java.lang.IllegalStateException

at java.util.ArrayList$Itr.remove(Unknown Source)

at javabase.collection.ListIteratorModificationTest.main(ListIteratorModificationTest.java:71)

线性表(List)



注:1、两层为接口,三层为类/2、虚线框为抽象类,实现框为具体类。3、红色框为线程安全类。

Collection<E>接口

代表一组元素的容器,提供操作其中元素的接口

he root interface in the collection hierarchy. A collection represents a group of objects, known as its elements.

AbstractCollection<E>抽象类

对容器的实现提供大致的轮廓,简化子类的实现。

This class provides a skeletal implementation of the Collection interface, to minimize the effort required to implement this interface.

List<E>接口

有序容器的接口。

可以将元素插入到特定位置,也可以根据位置来访问特定元素。

An ordered collection (also known as a sequence). The user of this interface has precise control over where in the list each element is inserted. The user can access elements by their integer index (position in the list), and
search for elements in the list.

AbstractList<E>抽象类

对随机访问的特性的有序容器的实现提供大致的轮廓,简化子类实现。而顺序访问的特性在AbstractSequential<E>类中定义。
This class provides a skeletal implementation of the List interface to minimize the effort required to implement this interface backed by a "random access" data store (such as an array). For sequential access data (such as
a linked list), Abstract Sequential List should be used in preference to this class.

ArrayList<E>类

大小自动扩展的数组,支持随机访问特性。

Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null.
In addition to implementing the List interface, this class provides methods to manipulate the size of the array that is used internally to store the list. (This class is roughly equivalent to Vector, except that it is unsynchronized.)

ArrayList示意图:



存储结构

/**The array buffer into which the elements of the ArrayList are stored.
The capacity of the ArrayList is the length of this array buffer.*/
private transient Object[] elementData;/

由于ArrayList中的元素是利用数组来存取的,因此ArrayList具有随机存取特性,当然也就需要连续的存储空间。

自动扩容

public boolean add(E e) {
ensureCapacityInternal(size + 1);  // Increment modCount!!增加元素时,检测容量是否足够。</span>
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
modCount++;
// overflow-conscious code
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);//将原始数据复制到新开辟的数组中,并更新缓存数组的引用
}
因为,ArrayList会进行容量扩充,我们不必担心容量不够。但是,由于容量扩充的存在,ArrayList可能将旧的数据重新移动到新开辟的数组中,从而会导致大量的复制操作,这会影响效率。

Vector<E>类

实现类似ArrayList,但是是线程安全的。

An ordered collection (also known as a sequence). The user of this interface has precise control over where in the list each element is inserted. The user can access elements by their integer index (position in the list), and search for elements in the list.

部分实现

public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);

return elementData(index);
}
public synchronized Iterator<E> iterator() {
return new Itr();
}

Vector的实现,将能够修改Vector底层缓存数组的关键方法都设置为synchronized,即进行同步访问。这样保证了线程安全,但是因为几乎任何访问操作都需要进行加锁,因此效率低下。

注意:即使,所有方法都设置为synchronized,对Vector的使用也并非是绝对线程安全的。

public class VectorUnsafeTest {
public static void main(String[] args){
final Vector<Integer> vector=new Vector<>(Arrays.asList(1,2,3,4,5,6,7));
for(int i=0;i<10;i++){
new Thread(new Runnable(){
@Override
public void run(){
int count=100;
while(count--!=0){
//synchronized(vector.getClass()){
for(int i=0;i<vector.size();i++){
System.out.print(vector.get(i));
}
//}
}
}
}).start();
}
for(int i=0;i<10;i++){
new Thread(new Runnable(){
@Override
public void run(){
//synchronized(vector.getClass()){ 额外的同步操作,将会确保线程安全性
int count=100;
while(count--!=0){
vector.remove(vector.size()-1);
vector.add(1);
}
//}
}
}).start();
}
}
}
上如程序运行,将会报很多ArrayIndexOutOfBoundsException异常。
前十个线程,虽然调用size()获取到的大小是准确的,但是,当他们按照获取到的大小依次访问元素时,可能有些元素已经被删除了,当然会报数组索引越界错误。
因此,对于Vector,我们只能说它保证了单个操作的线程安全性,对于特性序列的访问还是需要额外的同步机制来保证线程安全。

Stack<E>

一个先进后出的数据结构,该类只是对Vector类的接口进行适配,即设计模式中的适配器模式,特性参考Vector。

The Stack class represents a last-in-first-out (LIFO) stack of objects. It extends class Vector with five operations that allow a vector to be treated as a stack. The usual push and pop operations are provided, as well as a method to peek at the top item on
the stack, a method to test for whether the stack is empty, and a method to search the stack for an item and discover how far it is from the top.

AbstractSequentialList<E>抽象类

对顺序访问容器的实现提供大致的轮廓,简化子类实现。

This class provides a skeletal implementation of the List interface to minimize the effort required to implement this interface backed by a "sequential access" data store (such as a linked list). For random access data (such as an array), AbstractList should
be used in preference to this class.

LinkedList<E>类

Doubly-linked list implementation of the List and Deque interfaces. Implements all optional list operations, and permits all elements (including null).

存储结构

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;
}
}
不用多讲,这里就是我们在数据结构中常说的双向链表的实现,离散存储,空间利用率高,但是不支持随机访问特性。
LinkedList示意图



Queue



Queue<E>接口

队列,一个先进先出的数据结构。除了基本的容器操作外,还额外提供入队、出队、判空检查等队列特性相关的操作。但是,我们应该优先考虑使用队列特性相关的操作。

A collection designed for holding elements prior to processing.Besides basic Collection operations,queues provide additional insertion, extraction, and inspection operations.

Deque<E>接口

双端队列,在线性容器的两端都允许添加和移除元素,而队列中一般是只允许在一端添加而另一端删除。

A linear collection that supports element insertion and removal at both ends. The name deque is short for "double ended queue" and is usually pronounced "deck". Most Deque implementations place no fixed limits on the number of elementsthey may contain, but
this interface supports capacity-restricted deques as well as those with no fixed size limit.

ArrayDeque<E>类

基于数组实现的动态扩容的双端队列。

1、非线程安全

2、且不允许null。

3、该类用作栈时比Stack<E>容器要快,因为后者是同步容器。

4、该类用作队列时比LinkedList快,后者是双向链表,指针维护相对耗时,并且不是连续存储。

Resizable-array implementation of the Deque interface. Array deques have no capacity restrictions; they grow as necessary to support usage. They are not thread-safe; in the absence of external synchronization, they do not support concurrent access by multiple
threads. Null elements are prohibited. This class is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue.

存储结构

private transient E[] elements;
和ArrayList一样,底层是通过一个缓存数组来存放元素的。

动态扩容

public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();//扩容
}
private void doubleCapacity() {
assert head == tail;
int p = head;
int n = elements.length;
int r = n - p; // number of elements to the right of p
int newCapacity = n << 1;
if (newCapacity < 0)
throw new IllegalStateException("Sorry, deque too big");
Object[] a = new Object[newCapacity];//开辟新的空间,容量*2
System.arraycopy(elements, p, a, 0, r);//复制数据
System.arraycopy(elements, 0, a, r, p);//复制数据
elements = (E[])a;
head = 0;
tail = n;
}
因此,与ArrayList的扩容原理类似,还是开辟新的数据并复制数据,然后调整各种游标的值,需要连续存储空间。

LinkedList<E>

之前提到,其实现就是根据常说的双向链表。离散存储,对于队列这种对随机存储特性要求不高的数据结构而言,链表比较合适。

C++ STL的双端队列



C++中采用的离散存储与连续存储相结合的方式实现,对于空间的利用效率更高,扩充容量也不会像Java 双端队列那样大量的复制元素而产生长时间延迟。但是实现复杂,大量内部游标需要维护,特别是迭代器的实现也相对更加复杂。

以上三种方式,各有千秋,只能说需要根据具体的应用场景选择合适的实现方式。

Map



Map<K,V>接口

映射表,将键映射到值。键不能重复,每个键最多映射一个元素。

An object that maps keys to values. A map cannot contain duplicate keys;each key can map to at most one value.

AbstractMap<K,V>抽象类

对Map接口的实现提供了一个大致的轮廓,简化子类的实现。

This class provides a skeletal implementation of the Map interface, to minimize the effort required to implement this interface.

HashMap<K,V>类

基于哈希表实现的映射表。
1、该实现允许key为null,并且允许value为null。
2、HashMap几乎等同于HashTable,区别在于,HashMap是非同步的,并且key允许null。
3、该实现并不保证Map中的元素具有特定的顺序。
4、并且在运行过程中,元素之间的相对顺序也会随之变化。
Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except
that it is unsynchronized and permits nulls.) This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
HashMap示意图



存储结构

static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认填充因子0.75
transient Entry[] table; //存放Entry的数组,每个Entry就是一个链表的表头
transient int size;
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];//初始化存放Entry的数组大小
init();
}
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;//很明显,Entry就是一个链表,可以想象采用链地址法避免哈希冲突
final int hash;

......
}



因为存储结构是哈希表+链表,显然我们不能保证key具有特定的顺序,也就是元素之间没有特定顺序。

元素添加

public V put(K key, V value) {
if (key == null)//这里可以看出,允许key为null
return putForNullKey(value);
int hash = hash(key.hashCode());//根据key对象hashCode()函数的返回值作为哈希函数的输入来计算哈希值,哈希函数是一个本地方法
int i = indexFor(hash, table.length);//根据哈希值计算元素映射的位置(取余数)
for (Entry<K,V> e = table[i]; e != null; e = e.next) {//显然是链地址法解决哈希冲突,迭代映射在同一位置的所有元素,判定key是否已经存在。
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//存在就替换
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}

modCount++;
addEntry(hash, key, value, i);//不存在,将元素添加到指定位置的链表中
return null;
}
static int indexFor(int h, int length) {
return h & (length-1);//默认通过取余函数来计算映射位置
}


void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);//采用头插法插入元素
if (size++ >= threshold)
resize(2 * table.length);//扩充容量,2的指数增长
}

我们可以看出,HashMap是通过链地址法避免哈希冲突的,并且是具有动态扩容的特性。

扩容

void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}

Entry[] newTable = new Entry[newCapacity];//扩充哈希表的长度
transfer(newTable);//并将旧表中的数据转移到新表中
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}

void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {//迭代所有链表的表头
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {//迭代每个链表中的所有节点
Entry<K,V> next = e.next;
int i = indexFor(e.hash, newCapacity);//重新计算映射位置,然后采用头插法
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
不难发现,每当容量扩充时,会将原来哈希表中的所有数据重新映射到新的哈希表中。这也是为什么元素之间相对顺序得不到保证的原因。原来映射在同一个位置的key,哈希表的长度发生变化时,很可能不再映射在同一位置。此外,之前key 1映射的位置在key 2之前,扩容后,可能key 1映射的位置变成key
2之后,比如key 1计算的hash值为9,key 2哈希值为19。当哈希表长度为8时,key 1映射在索引下标为1处,而key 2映射在索引下标为3处。当哈希表长度扩张为16后,key 1映射的索引下标为9,而key 2映射索引下标仍然是3。

元素查找

public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];//先找key到在哈希表中映射的位置,然后在链表中顺序查找
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
哈希函数的计算为O(1)。由于填充因子<1,在平均情况下,链表的长度为O(1),因此查找的时间复杂度为O(1)。

元素删除

与元素查找同理,先找到key在哈希表中映射的位置,然后再O(1)长度的链表中进行链表删除操作,时间复杂度O(1)。

LinkedHashMap<K,V>

继承自HashMap。
1、与HashMap的区别在于,它将HashMap中的所有Entry(注意是所有,不仅仅是在table[]数组中的Entry(表头))通过一个双向链表进行链接。这样,HashMap也具有了顺序迭代的特性。
2、但是,其迭代顺序是元素键入的顺序,而不是某种排序规则。
3、此外,如果我们移除元素后再重新插入,那么其顺序为重新插入时的时间序。
Hash table and linked list implementation of the Map interface,with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries. This
linked list defines the iteration ordering,which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map. (A key k is reinserted into a map m if m.put(k,
v) is invoked when m.containsKey(k) would return true immediately prior to the invocation.)
LinkedHashMap示意图



存储结构

private transient Entry<K,V> header;//链表表头
private static class Entry<K,V> extends HashMap.Entry<K,V> {
// These fields comprise the doubly linked list used for iteration.
Entry<K,V> before, after;//除了继承自HashMap.Entry<K,V>的属性外,额外多出实现双向链表的指针
......
}

示例代码:
public class LinkedHashMapTest {
public static void main(String[] args) {
Map<Integer,Integer> map=new LinkedHashMap<Integer,Integer>();
map.put(1, 1);map.put(3, 3);map.put(2, 2);//插入顺序
for(Integer key:map.keySet()){
System.out.print(key+" ");
}
System.out.println();
map.put(1, 3);//重新插入,不影响相对顺序
for(Integer key:map.keySet()){
System.out.print(key+" ");
}
System.out.println();
map.remove(1);//移除后再重新插入,其顺序以重新插入的时间为准
map.put(1, 8);
for(Integer key:map.keySet()){
System.out.print(key+" ");
}
}
}
输出:
1 3 2
1 3 2
3 2 1

Dictionary<K,V>抽象类

该抽象类类似Map接口,定义了映射表的基本操作接口。key和value都不允许为null。
注意:该类已被废弃,新的实现应该实现Map接口。

The Dictionary class is the abstract parent of any class, such as Hashtable, which maps keys to values. Every key and every value is an object. In any one Dictionary object, every key is associated with at most one value. Given a Dictionary and a key, the associated
element can be looked up.Any non-null object can be used as a key and as a value.

Hashtable<K,V>类

同HashMap一样,也是利用哈希表+链表实现key到value的映射。
1、但是与HashMap的区别在于,key和value都不许为null。
2、HashTable还是同步容器,其核心方法都采用synchronized关键字修饰。

This class implements a hash table, which maps keys to values. Any non- null object can be used as a key or as a value.

private transient Entry[] table;//所有链表的头结点
private static class Entry<K,V> implements Map.Entry<K,V> {
int hash;
K key;
V value;
Entry<K,V> next;//链表
......
}

SortedMap<K,V>接口

对keys进行全局排序的Map。
1、默认我们根据key的内在的排序规则(key的类对Comparable接口的实现)对keys进行排序。此外,我们可以在创建SortedMap时,指定特定的比较器来自定义key的排序规则。
2、当我们迭代entrySet、keySet或者values时,这种排序规则将会显现出来。
A Map that further provides a total ordering on its keys. The map is ordered according to the plain Comparable natural ordering of its keys, or by a Comparator typically provided at sorted
map creation time. This order is reflected when iterating over the sorted map's collection views (returned by the entrySet, keySet and values methods). Several additional operations are provided to take advantage of the ordering. (This interface is the
map analogue of SortedSet.)

NavigableMap<K,V>接口

提供访问目标元素相邻元素的接口,比如小于目标元素中的最大值,大于目标元素中的最小值等等。

A SortedMap extended with navigation methods returning the closest matches for given search targets.

TreeMap<K,V>

基于红黑树实现的映射表。红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。

1、因为TreeMap是基于红黑树(一种二叉查找树),我们知道二叉查找树的中序遍历是递增排序的,因此Key就具有了内在排序。至于,其中的key如何排序,取决于你如何创建TreeMap,默认TreeMap通过key的内在排序规则排序(即key的类对Comparable接口的实现),但是你也可以在创建时指定特定的比较器来指定特定的排序规则。

A Red-Black tree based NavigableMap implementation. The map is sorted according to the plain Comparable natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.

存储结构

private transient Entry<K,V> root = null;
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left = null;//显然存储结构是树形结构
Entry<K,V> right = null;
Entry<K,V> parent;
boolean color = BLACK;
......
}


虽然说,我们获取了对元素顺序访问的能力。甚至可以自定义排序规则(相比LinkedHashMap中基于插入时间的排序要强得多),但是又得必有失。由于,平衡二叉树的高度为O(log n),因此我们查找元素的时间复杂度就退化成了O(log n)。因此,我们在灵活的顺序迭代元素和O(1)时间内的查找特性,这两项之间要做好取舍。

Set



Set<E>接口

不包含重复元素的容器,即集合。
1、判定元素的重复是利用对象的equals()方法来进行的。
2、集合允许null元素,并且最多允许一个null元素。

A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element. As implied by its name, this interface models the mathematical set abstraction.

AbstractSet<E>抽象类

对Set接口的实现提供一个大致的轮廓,简化子类实现。

This class provides a skeletal implementation of the Set interface to minimize the effort required to implement this interface.

HashSet<E>类

利用哈希表实现的无序集合。(其底层是利用HashMap实现的)
1、所谓无序集合,即集合中的元素没有特定的排序规则,对于元素的迭代访问不保证特定顺序。
2、随着集合元素的变化,元素之间的相对顺序可能会发生改变改变。
3、该类允许null值。

This class implements the Set interface, backed by a hash table (actually a HashMap instance). It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time. This class permits
the null

element.

存储结构

private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}

很明显,这里的HashSet底层就是用HashMap实现的。因为HashMap的key要求唯一约束,与集合Set的特性一致,因此HashSet的实现,仅仅只是对HashMap的接口进行了适配,即设计模式里面的适配器模式。

部分实现

private static final Object PRESENT = new Object();
public Iterator<E> iterator() {
return map.keySet().iterator();
}
public boolean contains(Object o) {
return map.containsKey(o);
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
因此HashSet拥有的特性依赖于HashMap中key的特性,具体内容参看HashMap源码。内层的HashMap中,每个元素的value都是指向一个静态Object对象的引用,如果HashSet的元素(也就是HashMap的key)本身大小很小,那么HashMap中value的额外引用所产生的开销,我们应该时刻留意。

注:因为Set容器的实现都是基于对应Map容器(Map容器的key)的实现,因为,具体解释和特性参考Map容器。

LinkedHashSet<E>类

该类仅仅是对LinkedHashMap的一个适配。其特性参照LinkedHashMap。
public LinkedHashSet() {
super(16, .75f, true);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}//父类HashSet中



SortedSet<E>接口

元素具有特性顺序的集合。

A Set that further provides a total ordering on its elements.The elements are ordered using their plain Comparable natural ordering, or by a Comparator typically
provided at sorted set creation time. The set's iterator will traverse the set in ascending element order. Several additional operations are provided to take advantage of the ordering.
(This interface is the set analogue of SortedMap.)



NavigableSet<E>接口

提供访问目标元素相邻元素的接口,比如小于目标元素中的最大值,大于目标元素中的最小值等等。

TreeSet<E>类

对元素特工特定排序规则的集合。具体参考TreeMap的性质。

A NavigableSet implementation based on a TreeMap. The elements are ordered using their plain Comparable natural ordering, or by a Comparator provided at set creation time, depending on which constructor is used.

存储结构

private transient NavigableMap<E,Object> m;
public TreeSet() {
this(new TreeMap<E,Object>());//内部是利用TreeMap实现
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息