java容器的数据结构-ArrayList,LinkList,HashMap
ArrayList:
初始容量为10,底层实现是一个数组,Object[] elementData
自动扩容机制,当添加一个元素时,数组长度超过了elementData.leng,则会按照1.5倍进行扩容
private void grow() {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
elementData = Arrays.copyOf(elementData, newCapacity);
}contains()方法的实现 ,注意ArrayList中是可以放null对象的
public boolean contains(Object o) {
return indexOf(o) >= 0;
}public int indexOf(Object o) {
if (o == null) { //首先判断对象是否为null,不是的话才可以执行o.equals(elementData[i])
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;
}ArrayList如何转化成数组呢?
* <p>This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all of the elements in this list in
* proper sequence
*/
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}随机获取某个位置的元素
/**
* Returns the element at the specified position in this list.
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
} @SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}添加一个元素,首先判断是否需要扩容
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}retainAll()方法,取两个集合的交集
/**
* Retains only the elements in this list that are contained in the
* specified collection. In other words, removes from this list all
* of its elements that are not contained in the specified collection.
*/
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}遍历整个容器
/**
* Returns an iterator over the elements in this list in proper sequence.
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
} private class Itr implements Iterator<E> {
int cursor; // index of next element to return
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
int i = cursor;
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
}
LinkedList:基于链表实现,插入和删除非常高效,只需要改变指针指向的位置,不需要像ArrayList那样整体挪动元素。LinkedList中主要提供了三个基本属性,size,first,last,初始容量size为0,可以简单的把first,last理解为两个指针,分别指向第一个节点和最后一个节点。
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null) */
transient Node<E> first; //first本身也是一个Node,没有previous元素,仅仅是指向队列首元素的标志
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null) */
transient Node<E> last; //last本身也是一个Node,没有next元素,仅仅是指向队列末尾元素的标志链表中每一个元素都是一个Node(节点)
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;
}
}在链表的某个位置前插入一个新的节点,要让这三个节点的指针互相指向。
/**
* Inserts element e before non-null Node succ.
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}让元素成为链表中的第一个元素,该方法只用于研究使用
/**
* Links e as first element.
*/
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f); //创建了一个节点,prev为null,last为原先的第一个元素
first = newNode; //让first指针指向第一个元素 newNode
if (f == null)
last = newNode; //如果容器里开始一个元素都没有,则让last也指向新添加的元素newNode
else 如果容器里开始就已经有元素了,则first此时已经变为第二个元素
f.prev = newNode;
size++;
modCount++;
}获得容器的第一个元素
/**
* Returns the first element in this list.
*/
public E getFirst() {
final Node<E> f = first;
if (f == null) //如果容器里一个元素都没有,则抛出异常,如果有元素,则第一个元素就是first
throw new NoSuchElementException();
return f.item;
}获得容器中的最后一个元素
/**
* Returns the last element in this list.
*/
public E getLast() {
final Node<E> l = last;
if (l == null) ////如果容器里一个元素都没有,则抛出异常,如果有元素,则最后一个元素就是last
throw new NoSuchElementException();
return l.item;
}删掉容器中的第一个元素
/**
* Unlinks non-null first node f.
*/
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next; //获得下一个节点,当删掉第一个节点后,后一个节点就变成第一个节点了
f.item = null; //将节点中的item赋值为空
f.next = null; // 将指针next赋值为空
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}删掉容器中间的某个元素
/**
* Removes the first occurrence of the specified element from this list,
* if it is present.
*/ public boolean remove(Object o) {
if (o == null) { //首先判断删除的Element是否为空
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
} /**
* Unlinks non-null node x.
*/
E unlink(Node<E> x) { 该方法至关重要,删除中间节点的操作
final E element = x.item;
final Node<E> next = x.next; //获得当前结点的下一个节点
final Node<E> prev = x.prev; //获得当前结点的上一个节点
if (prev == null) {
first = next; //如果prev是空值的话,则说明当前删的是第一个节点,那么删除后first
} else { 指针将指向x的下一个节点next
prev.next = next; //如果不是的话,则有左边的指针指向关系
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null; //总之,要删掉节点x,需要将prev,next,item都赋值为null
size--;
modCount++;
return element;
}判断是否包含某个元素,元素在哪个具体位置(index)
public boolean contains(Object o) {
return indexOf(o) != -1;
} /**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
*/
public int indexOf(Object o) {
int index = 0; //设置一个计数器
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++; //每次循环,如果不是得话就++
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
遍历整个容器,返回ListIterator双向迭代器
/**
* The list-iterator is <i>fail-fast</i>: if the list is structurally
* modified at any time after the Iterator is created, in any way except
* through the list-iterator's own {@code remove} or {@code add}
* methods, the list-iterator will throw a
* {@code ConcurrentModificationException}. Thus, in the face of
* concurrent modification, the iterator fails quickly and cleanly, rather
* than risking arbitrary, non-deterministic behavior at an undetermined
* time in the future.
*/
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件(快速失败行为)。
(注意:结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)
迭代器指向的位置是元素之前的位置,如下图所示:
这里假设集合List由四个元素List1、List2、List3和List4组成,当使用语句Iterator it = List.Iterator()时,迭代器it指向的位置是上图中Iterator1指向的位置,当执行语句it.next()之后,迭代器指向的位置后移到上图Iterator2所指向的位置。
Iterator迭代器包含的方法有:
hasNext():如果迭代器指向位置后面还有元素,则返回 true,否则返回false
next():返回集合中Iterator指向位置后面的元素
ListIterator迭代器包含的方法有:
hasNext():以正向遍历列表时,如果列表迭代器后面还有元素,则返回 true,否则返回false
hasPrevious():如果以逆向遍历列表,列表迭代器前面还有元素,则返回 true,否则返回false
next():返回列表中ListIterator指向位置后面的元素
previous():返回列表中ListIterator指向位置前面的元素
HashMap:默认初始化大小为16
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16负载因子为0.75
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;当超过length*factor时,HashMap就会自动扩容,按照两倍进行扩容。
HashMap底层由数组实现(具体来讲是数据加链表) Node<K,V>[] table;
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
往HashMap中放入一个Mapping
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key); //首先获得key的hash值
int i = indexFor(hash, table.length); //其次计算出应该放在table的哪个位置(桶)
for (Entry<K,V> e = table[i]; e != null; e = e.next) { //桶由链表构成
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value; //如果要插入的Mapping的Hash值和key与桶中的某个
e.recordAccess(this); Mapping完全相同,则替换掉它的value值
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
final int hash(Object k) {
int h = 0;
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
return h & (length-1);
}
当需要插入的key
为null
时,调用putForNullKey
方法处理:
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
当需要插入的
key
为null
时,调用putForNullKey
方法处理: private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value; //如果HashMap中有<null,value>这样的Node,则一定是放在table的
e.value = value; 首位置,因为null的hash值为0
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}添加一个Entry,首先判断是否需要扩容,size表示table(数组)当前的元素个数,扩容时按照两倍的方式扩容,之后还需要重哈希(rehash)
void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
真正意义上的添加Entry,首先获得链表原来的头节点e,然后构造一个新的节点,使其next指针指向e:
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
在HashMap中如何根据key获得相应的value值呢?
public V get(Object key) {
if (key == null) //首先判断key是否为null
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key); //首先获得key的hash值
for (Entry<K,V> e = table[indexFor(hash, table.length)];//获得key在table中的index
e != null;
e = e.next) {
Object k;
if (e.hash == hash && //如果完全相同,则返回key对应的Entry
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}