常用集合类
1.List
1)ArrayList
2)LinkedList
2.Map
1)HashMap
2)HashTable
3)TreeMap
4)LinkedHashMap
3.Set
1)HashSet
2)TreeSet
3)LinkedHashSet
ArrayList
1.List接口的可变数组的实现。实现了所有可选列表操作,
并允许包括null在内的所有元素
2.非线程安全
3.底层使用的数据结构为数组
4.适合查改,弱于增删
ArrayList实现分析
//用指定的元素替代此列表中指定位置上的元素,并返回以前位于该位置上的元素
public E set(int index,E element){
RangeCheck(index);//检查index是否合法
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
//将指定的元素添加到列表的尾部
//直接添加速度快
public boolean add(E e){
ensureCapacity(size+1);确保容量满足
elementData[size++]=e;
return true;
}
//将指定的元素插入此列表中的指定位置
//如果当前位置有元素,则向右移动当前位于该位置的元素以及所有后续元素(将其索引加1)
//涉及数组拷贝,插入速度不及add(E element)方法
public void add(int index,E element){
if(index>size||index<0)
throw new InddexOutOfBoundsException("Index:"+index+",Size"+size);
//如果数组长度不足,将进行扩容
ensureCapacity(size+1);
System.arraycopy(elementData,index,elementData,index+1,size-index);
elementData[index] = element;
size++;
}
过程:abcd()-->ab()cd-->ab(e)cd
//移除此列表中指定位置上的元素
//涉及数组拷贝
public E remove(int index){
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size-index-1;
if(numMoved>0)
System.arraycopy(elementData,index+1,elementData,index,numMoved);
elementData[--size]=null;
return oldValue;
}
过程:abcd-->(删除b元素)acdd-->acd
//数组扩容,按照1.5倍方式扩充。涉及数组拷贝,速度慢
public void ensureCapacity(int minCapacity){
modCount++;
int oldCapacity = elementData.length;
if(minCapacity>oldCapacity){
int newCapacity = (oldCapacity*3)/2+1;
if(newCapacity<minCapacity)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData,newCapacity);
}
}
LinkedList
1.List接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括null)
2.实现Deque接口,为add,poll提供先进先出队列操作以及其他堆栈和双端队列操作
3.非线程安全
4.适合增删,弱于查改
LinkedList数据结构
private transient Entry<E>header = new Entry<E>(null,null,null);
private static class Entry<E>{
E element;
Entry<E>next;//下一个节点
Entry<E>previous;前序节点
//构造方法:目标对象paramE将被放置在paramEntry1之前,paramEntry2之后
Entry(E paramE,Entry<E>paramEntry1,Entry<E>paramEntry2){
this.element = paramE;
this.next = paramEntry1;
this.previous = paramEntry2;
}
}
双端队列 (previous)<--Entry-->(next)
LinkedList实现分析
//根据序号获取Entry对象
private Entry<E>entry(int paramInt){
if((paramInt<0)||(paramInt>=this.size)){
throw new IndexOutOfBoundsException("Index:"+paramInt+"Size:"+this.size);
}
Entry localEntry = this.header;
int i;
//最多遍历size/2个元素
if(paramInt<this.size>>1){
for(i=0;i<=paramInt;i++)
localEntry = localEntry.next;
}else{
for(i=this.size;i>paramInt;i--)
localEntry = localEntry.previous;
}
return localEntry;
}
//要添加的元素:paramE
//目标对象:paramEntry
//插入速度快
private Entry<E>addBefore(E paramE,Entry<E>paramEntry){
//要添加的对象,设置其previous和next
Entry localEntry = new Entry(paramE,paramEntry,paramEntry.previous);
localEntry.previous.next = localEntry;
localEntry.next.previous = localEntry;
this.size +=1;
this.modCount +=1;
return localEntry;
}
LinkedList添加元素
//指定位置添加元素,需要先找到index的元素,然后添加
public void add(int index,E element){
addBefore(element,(index==size?header:entry(index)));
}
//队首添加元素
public void addFirst(E paramE){
addBefore(paramE,this.header.next);
}
//队尾添加元素
public void addLast(E paramE){
addBefore(paramE,this.header);
}
LinkedList删除元素
//删除速度快
private E remove(Entry<E>e){
if(e == header)
throw new NoSuchEllementException();
E result = e.element; //保留将被移除的节点e的内容
e.previous.next = e.next;//将前一节点的next引用赋值为e的下一个节点
e.next.previous = e.previous;//将e的下一个节点的previous赋值为e的上一个节点
e.next = e.previous = null;//解除e节点对前后节点的引用
e.element = null;
size--;
modCount++;
return result;
}
public E remove(){
return removeFirst();//删除第一个元素
}
public E remove(int index){
return remove(entry(index));//删除第index个元素
}
public E removeFirst(){
return remove(header.next);//删除头部下一个
}
public E remove()Last{
return remove(header.previous);//删除最后一个
}
List的适用范围
1.ArrayList适用于对于数据查询修改大于数据增删的场合
2.LinkedList适用于对于数据增删大于数据查询的场合
HashMap
1.基于哈希表的Map实现。提供所有可选的映射操作,并允许使用null值和null键
2.非线程安全
3.不保证映射的顺序,特别是它不保证该顺序恒久不变
HashMap数据结构
1.通过Hashcode找到数组中的某一元素
2.通过key的equals方法在链表中找到key对应的value
transient Entry[] table;
static class Entry<K,V>implements Map.Entry<K,V>{
final K key;
V value;
Entry<K,V>next;
fianl int hash;
······
}
HashMap实现分析
public V put(K key,V value){
if(key == null)//null键视为相同的键
return putForNullKey(value); //根据key的keyCode重新计算hash值
int hash = hash(key.hashCode()); //搜索指定hash值在对应table中的索引
int i = indexFor(hash,table.length);
//如果i索引处的Entry不为null,通过循环不断遍历e元素的下一个元素
for(Entry<K,V>e = table[i];e!=null;e =e.next){
Object k;
//key重复出现则更新其value
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;
}
//hash函数,加入了高位计算,防止低位不变,高位变化时,造成的hash冲突
static int hash(int h){
h^=(h>>>20)^(h>>>12);
return h^(h>>>7)^(h>>>4);
}
void addEntry(int hash,K key,V value,int bucketIndex){
//获取指定bucketIndex索引处的Entry
Entry<K,V>e = table[bucketIndex];
//将创建的Entry放入bucketIndex索引处,并让新的Entry指向原来的Entry
table[bucketIndex] = new Entry<K,V>(hash,key,value,e);
//如果Map中的key-value对的数量超过了极限
if(size++ >=threshold)
//把table对象的长度扩充到原来的2倍
resize(2*table.length)
}
//根据hash值查找对应的table位置
static int indexFor(int h,int length){
return h&(length-1)
}
//put的反过程
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)];e!=null;e=e.next){
Object k;
if(e.hash==hash&&((k=e.key)==key||key.equals(k)))
return e.value;
}
return null;
}
HashTable的特点
1.HashMap和HashTable采用相同的存储机制,二者的实现基本一致
2.不允许有null值的存在
3.HashTable是线程安全的,内部的方法基本都是synchronized
4.迭代器具有强一致性
TreeMap
1.Map接口的树实现
2.不允许有null值的存在
3.非线程安全
4.键值有序
TreeMap数据结构:红黑树
TreeMap实现分析
1.Entry是红黑树的节点,它包含了红黑树的6个基本组成成分:key键,value键
left左孩子,right右孩子,parent父节点,color颜色。Entry节点根据key进行排序
Entry节点包含的内容为value
2.红黑树排序时,根据Entry中的key进行排序。Entry中的key比较大小是根据比较器
comparator来进行判断的
TreeMap的优势
1.空间利用率高
1)HashMap的数组大小必须为2的n次方
2)TreeMap中树的每一个节点就代表了一个元素
2.性能稳定
1)Hash碰撞会导致HashMap查询开销提高
2)HashMap扩容时会rehash,开销高
3)TreeMap的操作均能在O(log n)内完成
LinkedHashMap
1.Map接口的哈希表和链接列表实现,提供所有可选的映射操作,并允许使用null值和null键
2.非线程安全
3.具有可预知的迭代顺序
LinkedHashMap实现分析
void addEntry(int hash,K key,V value,int bucketIndex){
//调用create方法,将新元素以双向链表的形式加入到映射中
createEntry(hash,key,value,bucketIndex);
Entry<K,V>eldest = header.after;
if(removeEldestEntry(eldest)){
removeEntryForKey(eldest.key);
}else{
if(size>=threshold)
resize(2*table.length);
}
}
void createEntry(int hash,K key,V value,int bucketIndex){
HashMap.Entry<K,V>old = table[bucketIndex];
Entry<K,V>e=new Entry<K,V>(hash,key,value,old);
table[bucketIndex]=e;
//调用元素的addBrefore方法,将元素加入到双向链接列表
e.addBefore(header);
size++
}
public V get(Object key){
//调用父类HashMap的getEntry()方法,取得要查找的元素
Entry<K,V>e = (Entry<K,V>)getEntry(key);
if(e==null)
return null;
//记录访问顺序
e.recordAccess(this);
return e.value;
}
void recordAccess(HashMap<K,V>m){
LinkedHashMap<K,V>Im = (LinkedHashMap<K,V>)m;
//如果定义了LinkedHashMap的迭代顺序为访问顺序,则删除以前位置上的元素,并将最新访问的元素添加到链表表头
if(Im.accessOrder){
Im.modCount++;
remove();
addBefore(Im.header);
}
}
Map的适用范围
1.HashMap适用于一般的键值映射需求
2.HashTable适用于有多线程并发的场合
3.TreeMap适用于要按照键排序的迭代场合
4.LinkedHashMap适用于特殊顺序的迭代场合(如LRU算法)
HashSet
1.实现Set接口,由哈希表支持,允许使用null元素
2.非线程安全
3.不保证set的迭代顺序,特别是不保证该顺序恒久不变
HashSet实现分析
//底层使用HashMap来保存HashSet中所有元素
private transient HashMap<E,Object>map;
//定义一个虚拟的Object对象作为HashMap的value
private static final Object PRESENT = new Object();
//借助HashMap的add方法来添加
//HashMap的add方法可以返回该key之前的value值,如果为null则说明之前尚未添加该key,即该HashSet可以添加该元素
public boolean add(E e){
return map.put(e,PRESENT)==null;
}
//借助HashMap的方法来查找
public boolean countains(Object o){
return map.containKey(o);
}
Set的特点
1.HashSet通过HashMap实现
2.TreeSet通过TreeMap实现
3.LinkedHashSet通过LinkedHashMap实现
4.Set类与Map类拥有近似的使用特性