有人说Java很简单都是api调用而已,没啥技术.其实光是Jdk提供的api中很多思想和算法都用在里面,比如集合中的List,Set,Queue,Map,多线程,排序,noi等等.这里我从JDK源码开始简单分析这些项目中常使用的类是如何实现的.下面先简单介绍一些集合类型.
一.ArrayList
这就是地址上连续的数组.里面存储数据的是一个Object数组. size是当前数组装的对象的数量.默认初始化Object大小是10.
private static final int DEFAULT_CAPACITY = 10;
private transient Object[] elementData;
private int size;
在add一个元素的时候先判断Object数组有没有创建,没有的话先创建数组,然后判断数组size+1 > 数组的长度之后会进行扩容, int newCapacity = oldCapacity + (oldCapacity >> 1); 扩容的大小是原来大小的1.5倍.
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
ensureExplicitCapacity(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);
}
取得时候就比较简单了,先判断边界,然后用下标去取.
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
二.LinkedList
LinkedList内部是由双链表实现的,有一个静态内部类实现了Node节点,可以看到里面有next和prex两个指针.
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;
}
}
size表示当前链表长度. 有两个变量保存链表头部,和链表结尾.这样可以从头插入(取出,删除)节点或从尾插入节点. 普通的add方法只是在尾节点插入节点.
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
public void addFirst(E e)
public void addLast(E e)
public E removeFirst()
public E removeLast()
public boolean add(E e) {
linkLast(e);
return true;
}
三.HashMap
HashMap的核心Entry是一个节点,有一个指针可以指向下一个Entry.
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
HashMap有一个Entry数组,默认的加载因子为0.75,默认初始Entry数组长度为16.
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
HashMap在put的时候,先判断数组是否为空,是空进行初始化,数组默认长度为16.根据元素的hashCode方法去计算hash值然后找到对应的下标.当前Entry数组下标的元素,然后从这个元素开始遍历,如果找到hash相等且equals也相等那么就把Entry的value值替换掉.若没有找到hash相等且equals也相等的话,就生成一个新的Entry且把这个Entry放在table下标下,指针指向原来的下标元素.在生成新的Entry之前会去判断是否需要rehash进行扩容.当数组元素个数size>原始数组长度大小乘以load_fact 第一次扩容的时候在size > 16*0.75的时候.
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
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;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
HashMap get()方法就比较简单了,根据key的hashCode方法找到key在数组中的下标.然后从下标位置开始遍历Entry.从而找到Entry.
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
四.HashSet
其实HashSet就是利用HashMap去实现的,value值是一个固定的Object对象
private static final Object PRESENT = new Object();
private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
HashSet add 方法就是好调用的HashMap的put方法value就是Object对象.contains方法也是调用的HashMap的contains方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public boolean contains(Object o) {
return map.containsKey(o);
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
五.Hashtable
HashMap是存在并发问题的,Hashtable解决了并发问题,解决的方法也很简单,在几乎所有的方法前面加上sychronized,导致同一个Hashtable对象同一时刻只能有一个线程使用其中的一个方法,大大的降低了效率
public synchronized V put(K key, V value);
public synchronized V get(Object key) ;
public synchronized V remove(Object key) ;
public synchronized int size() ;
六.ConcurrentHashMap
ConcurrentHashMap里面有一个Segment数组,每一个Segment相当于一个Hashtable,这样就把锁分成了多了(默认是16),不同的Segment之间是没有并发问题的.
/**
* The segments, each of which is a specialized hash table.
*/
final Segment<K,V>[] segments;
static final class Segment<K,V> extends ReentrantLock implements Serializable {
transient volatile HashEntry<K,V>[] table;
static final class HashEntry<K,V> {
final int hash;
final K key;
volatile V value;
volatile HashEntry<K,V> next;
HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
/**
* Sets next field with volatile write semantics. (See above
* about use of putOrderedObject.)
*/
final void setNext(HashEntry<K,V> n) {
UNSAFE.putOrderedObject(this, nextOffset, n);
}
// Unsafe mechanics
static final sun.misc.Unsafe UNSAFE;
static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class k = HashEntry.class;
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}
}
get的时候先根据hash值找到segment下标,然后找到HashEntry.再遍历HashEntry链表,找到HashEntry.
public V get(Object key) {
Segment<K,V> s; // manually integrate access methods to reduce overhead
HashEntry<K,V>[] tab;
int h = hash(key);
long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
(tab = s.table) != null) {
for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
(tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
e != null; e = e.next) {
K k;
if ((k = e.key) == key || (e.hash == h && key.equals(k)))
return e.value;
}
}
return null;
}
ConcurrentHashMap有一部分代码没看懂,回头有时间再来整理.