Java集合

java集合

1.什么是集合?

Java中的集合collection指一个JAVA对象内部持有其他JAVA对象, 且提供对外接口,这样的Java对象称为集合。
集合的特点是接口(Interface)和实现类(Implementation)分离,并且可以使用泛型。

2. 集合接口及其对应的实现类

黑色为集合接口,黑色为对应的实现类
黑色表示集合,红色表示对应的实现类在这里插入图片描述

3.集合中元素的常见操作

集合中插入元素: bollean add( E element), Map: V put(K key, V value)
读取元素: Iterator, Map: V get(K key)
List: 有序集合,有两种实现类: ArrayList LinkedList, 有两种元素的访问方法:一是迭代器顺序访问元素, 二是使用整数索引随机访问元素,常用的随机访问方法:
void add(int index, E element)
void remove(int index)
E get(int index)
E set(int index, E element)
对于ArrayList,可以通过get,set方法随机访问,但是却不适于LinkedList, 链表结构可以使用Iterator接口实现快速增、删操作。以下代码来源《JAVA核心技术卷一》

import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;
import java.util.ListIterator;

public class LinkedListTest {
	public static void main(String[] args) {
		List<String> a = new LinkedList<>();
		a.add("Army");
		a.add("Carl");
		a.add("Erica");
		
		List<String> b = new LinkedList<>();
		b.add("Bob");
		b.add("KD");
		b.add("Frances");
		b.add("Gloria");
		
		// merge string from b to a
		ListIterator<String> aIter = a.listIterator();
		Iterator<String> bIter = b.iterator();
		
		while(bIter.hasNext()){
			if(aIter.hasNext()) aIter.next();
			aIter.add(bIter.next());
			
		}
		System.out.println(a);
		System.out.println(b);
		
		//remove every second word from b
		bIter =b.iterator();
		while(bIter.hasNext()) {
			bIter.next();//skip one element
	
			if(bIter.hasNext()) {
				bIter.next();//skip next element
				bIter.remove();//remove that element
			}
		}
		System.out.println(b);
		
		// bulk operation:remove all words in b from a
		
		a.removeAll(b);
		System.out.println(a);	
	}
}

散列集合

LinkedList ArrayList 可以按照一定的顺序给元素排序,如果需要查看某个元素,但是又忘记了位置,则需要遍历所有元素。当集合中元素数量很大的时候,程序执行效率就会很低。针对不太在意元素顺序的情况,Java集合提供了set接口,对应的实现类主要有: HashSet TreeSet

  1. HashSet
    在介绍HashSet之前有必要介绍一种常见的数据结构:Hashtable
  • Hashtable继承自Dictionary, 实现的接口有:Map<k,v> Cloneable java.io.Serializable,所以Hashtable保存的是key-value键值对,且均不能为null. Hashtable函数均为同步的,所以线程安全
public class Hashtable<K,V) extends Dictionary<K,V>  implememts Map<K,V>, Cloneable, java.io.Serializable{
	...
}
  • Hashtable通过”拉链法“实现哈希表,包含的成员变量:table count threshold loadFactor modCount
    • table是一个Entry[]数组,Entry表示单向链表。哈希表的”Key-Value" 存储在Entry数组
    • count表示Hashtable保存的键值对的数量
    • Threshlod是Hashtable的阈值,判断是否需要调整Hashtable的容量。Threshold = Capacity*LoadFactor
    • LoadFactor 加载因子
    • modCount用来实现fail-fast机制
  • Hashtable数据节点Entry的数据结构
private static class Entry<K,V> implements Map.Entry<K,V>{
// 哈希值
	int hash;
	K key;
	V value;
	//指向下一个Entry,即链表的下一个节点
	Entry<K,V> next;
	//构造函数
	protected Entry(int hash, K key, V value, Entry<K,V> next){
	this.hash = hash;
	this.key = key;
	this.value = value;
	this.next = next;
}
	protected Object clone(){
		return new Entry<K,V> (hash, key, value, (next == null ?null: (Entry<K,V>) next.clone()));
		}
	public K getKey(){
	retrun key;
	}
	public V value(){
		return value;
	}
	//设置value, 若value是null,则抛出异常
	public  V setValue(V value) {
		if(value ==null){
		throw new NullPointerException();
		V oldValue = this.value;
		this.calue = value;
		return oldValue;
	}
// 覆写equals()方法, 判断两个Entry是否相等
//若两个Entry的key value都相等,则认为他们相等
	public boolean equals(Object o){
		if( !(o instanceof Map.Entry)){
			return false;
		Map.Entry e= (Map.Entry) 0;
		return (key == null ? e.getkey() == null :key.equals(e.getKey())) && (value ==null ? e.getValue==null : value.equals(e.getValue()));
	}
	public int hashCode(){
		return hash ^ (value == null ? 0 : value.hashCode());
	}
	public String toString(){
		return key.toString() + "=" +value.toString();
	}
}
  • Hashtable构造函数
//默认构造函数
public Hashtable(){
//默认容量为11,加载因子0.75
	this(11,0.75f);
}
//制定容量大小
public Hashtable(int initialCapacity){
	this(initialCapacity,0.75f);
}
//指定容量大小和加载因子
public Hashtable(int initialCapacity, float loadFactor){
	if(initialCapacity < 0){
		throw new IllegalArgumentException("IllegalCapacity: " + initialCapacity);
	}
	if(loadFactor <=0 || Float.isNaN(loadFactor)){
		throw new IllegalArgumentException("IllegalLaod: " +loadFactor);
	if(initialCapacity ==0) 
		initialCapacity = 1;
		this.loadFactor = loadFactor;
		table = new Entry[initialCapacity];
		threshold = (int) (initialCapacity * loadFacotr);
	}
//包含子MAP的构造器
public Hashtable(Map<? extends K, ? extends V> t){
	this(math.max(2*t.size(), 11), 0.75f);
	//将子Map全部元素添加到Hashtable
	putAlll(t);
}
  • Hashtable常用的API
    • boolean contains(Object value)
    • boolean containsKey(Object key)
    • boolean containValue(Object value)
    • boolean equals(Object object)
    • Enumeration < V > elements()
    • Set<Entry<K, V> > entrySet()
    • V get(Object key)
    • int hashCode()
    • boolean isEmpty()
    • Set < K> keySet()
    • Emumeration < K> keys()
    • V put(K key, V value)
    • void putAll(Map< ? extends K, ? extends V> map)
    • V remove(Object key)
    • int size();
    • String toString()
    • Collection < V > values()
    // containsKey()源码
    public synchronized boolean  containsKey(Object key){
    	Entry tab[] = table;
    	int hash = key.hashCode();
    	//计算索引值
    	// %tab.length防止数据越界
    	int index = (hash & 0x7FFFFFFF) %tab.length;
    	// 找到key对应的Entry,然后在链表中找出哈希值和键值均相等的元素
    	for( Entry<K, V> entry = tab[index];  entry!= null; entry= entry.next){
    		if((entry.hash==hash) && e.key.equal(key){
    			retrun true;
    		}
    	}
    	return false;
    }
    
    //elements()作用是返回所有value的枚举对象
    public synchronized Enumeration<V> elements(){
    	return this.<V>getEnumeration(VALUES);
    }
    //获取Hashtable枚举类对象
    private <T> Enumeration<T> getEnumeration (int type){
    	if(count ==0){
    		return (Enumeration<T> emptyEnumerator;
    	}else{
    		return new Enumerator<T> (type, false);
    	}
    	private static Enumeration emptyEnumerator = new EmptyEnumerator();
    	//空枚举类
    	//当Hashtable实际大小为0, 通过Enumeration遍历Hashtable, 返回空枚举类对象
    	private static class EmptyEnumerator implements Enumeration<Object> {
    		EmptyEnumerator(){
    		}
    		public boolean hasMoreElements(){
    			return false;
    		}
    
    //Enumerator接口
    private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
    	//指向Hashtable的table
    	Entry[] table = Hashtable.this.table;
    	//hashtable的大小
    	int index = table.length;
    	Entry<K,V> entry = null;
    	Entry<K,V> lastReturned = null;
    	int type;
    	// iterator为ture, Enumerator 为迭代器,反之位Enumeration 枚举类
    	boolean iterator;
    	// 将enumerator用作迭代器时, 用来实现Fail-fast机制
    	protected int exceptedModCount = modCount;
    	Enumerator(int type, boolean iterator){
    		this.type= type;
    		this.iterator = iterator;
    	}
    	//遍历Table数据从末尾,直到找到不为Null的Entry
    	public boolean hasMoreElements(){
    		Entry<K,V> e =entry;
    		int i = index;
    		Entry[] t = table;
    		while(e ==null && i>0){
    			e = t[--i];
    		}
    		entry = e;
    		index = i;
    		return e!=null;
    	}
    	//获取下一个元素
    	public T nextElements(){
    		Entry<K,V> et = entry;
    		int i = index;
    		Entry[] t = table;
    		while(et==null && i>){
    			et = t[--i[;
    		}
    		entry = rt;
    		index =i;
    		 if(et ! = null){
    		 	Entry<K,V> e = lastReturned = entry;
    		 	 entry = e.next;
    		 	 return type == KEYS ? (T)e.key : (type == VALUES ? (T) e.value : (T) e);
    		 }
    		 throw new NoSuchElementsException("HashtableEnumrator");
    	}	
    
    //put()源码
    public synchronized V put( K key, V value){
    	//不能存放null 元素
    	if( value ==null){
    		throw new NullPointerException();
    	}
    	// 若Hashtable已存在键位key的键值对,用新value替换旧value
    	Entry tab[] = table;
    	int hash = key.hashCode();
    	int index = (hash &0x7FFFFFFF) % tab.length;
    	for(Entry<K,V> e = tab[index]; e !=null; e= e.next){
    		if((e.hash == hash) && e.key.equals(key)){	
    			V old = e.value;
    			e.value = value;
    			return old;
    		}
    	}
    	//若Hashtable不存在键为key的键值对
    	//1. 将修改统计数+1
    	modCount++;
    	///2.若Hashtable实际容量 > 阈值, 则调整Hashtable大小
    	if( count >= threshold){
    		rehash();
      	tab = table;
      	index = (hash & 0x7FFFFFFF) % tab.length;
      }
       // 3. 将Hashtable中Index位置的Entry保存到e
       Entry<K,V> e = tab[index];
       // 4. 创建新的Etry节点,并将新Entry插入Hashtable的index位置, 并设置e为新的Entry的下一个元素,即新entry为链表表头
       tab[index] = new Entry<K,V> (hash, key , value, e);
       //5. Hashtable容量+1
       count++;
       return all;
      }
    
    • Hashtable的遍历
      • 1 遍历Hashtable 键值对
      // 1. 根据entrySet()获取Hashtable的键值对的集合
      Integer integ = null;
      Iterator iter = table.entrySet().iterator();
      // 通过Iterator迭代器遍历第一步得到的集合
      while(iter.hasNext()){
      	Map.Entry entry = (Map.Entry) iter.next();;
      	//获取key
      	key = (String)enry.getKey();
      	integ = (Integer) entry.getValue();
      }
      
      • 2 通过Iteraotr遍历Hashtable键
      String key =null;
      Integer iteg = null;
      Iterator iter = table.keySet().iterator();
      while(iter.hasNext()){
      	key = (String) iter.next();
      	integ = (Integer) table.get(key);
      }
      
      • 3 通过Iterator遍历Hashtable的值
      Integer value = null;
      Collection c= table.values();
      Iterator iter = table.iterator();
      while( iter.hasNext()){
      	value = (Integer)iter.next();
      }
      
      • 4 通过Enumeration遍历Hashtable的键
      Enumeration emu = table.keys();
      while(emu.hasMoreElements()){
      	System,out,println(emu.nextElements());
      }
      
      • 5 通过Enumeration遍历Hashtable的值
      Enumeration  emu = table.elements();
      while(emu.hasMoreElements()){
      	System.out.printn(emu.nextElements());
      }
      
  • Hashtable遍历的测试实例
		import java.util.*;
		import java.util.Hashtable;

   public class Hash_Table_Test {
   public static void main(String[] args) {
   	int val =0;
   	String key = null;
   	Random r = new Random();
   	Integer value = null;
   	Hashtable<String, Integer> table = new Hashtable<>();
   	for(int i =0; i<12; i++) {
   		val = r.nextInt(100);
   		key =String.valueOf(val);
   		value = r.nextInt(5);
   		
   		table.put(key,value);
   		System.out.println("Key:" +key +"value:" +value);			
   	}
   	//通过entrySet()遍历
   	iteratorHashtableByEntryset(table);
   	//通过keySet()遍历Hashtable的key-value
   	iteratorHashtableByKeyset(table);
   	//通过Enumeration 遍历Hashtable 的key
   	enumHashtableKey(table);
   	//通过Enumeration遍历Hashtable 的value
   	enumHashtableValue(table);
   	//遍历Hashtable的value
   	iteratorHashtableJustValue(table);
   	
   }
   
   private static void iteratorHashtableByEntryset(Hashtable table) {
   	if(table==null) {
   		return ;
   	}
   	System.out.println("\niterator Hashtable by entrySet");
   	
   	String key = null;
   	Integer value = null;
   	Iterator iter = table.entrySet().iterator();
   	while(iter.hasNext()) {
   		Map.Entry entry = (Map.Entry) iter.next();
   		key = (String)entry.getKey();
   		value = (Integer)entry.getValue();
   		System.out.println(key + "-" + value.intValue());
   	}
   }
   private static void iteratorHashtableByKeyset(Hashtable table) {
   	if (table == null)
   		return ;
   	System.out.println("\niterator Hashtable by keySet");
   	
   	String key =null;
   	Integer value = null;
   	Iterator iter = table.keySet().iterator();
   	while(iter.hasNext()) {
   		key = (String)iter.next();
   		value =(Integer) ((Map) table).get(key);
   		System.out.println(key + "--" + value.intValue());
   	}	
   }
   private static void enumHashtableKey(Hashtable table) {
   	if (table == null) {
   		return ;
   	}
   	System.out.println("\nenumHashtableKey");
   	
   	Enumeration emu =table.keys();
   	while(emu.hasMoreElements()) {
   		System.out.println(emu.nextElement());
   	}
   }
   private static void enumHashtableValue(Hashtable table) {
   	if(table == null)
   		return ;
   	System.out.println("\nenumHashtableValue");
   	
   	Enumeration emu =table.elements();
   	while(emu.hasMoreElements()) {
   		System.out.println(emu.nextElement());
   	}
   }
   private static void iteratorHashtableJustValue(Hashtable table) {
   	Collection c = table.values();
   	Iterator iter = c.iterator();
   	while(iter.hasNext()) {
   		System.out.println(iter.next());
   	}
   }	
}

Hashtable是线程安全的, 相对而言,HashMap更为常见, HashSet的底层实现是HashMap, HashMap与Hashtable相似,下面做一简单介绍

  • HashMap
    • 初识HashMap
      HashMap存储的内容也是键值对
      HashMap extends AbstractMap, implements Map, Cloneable, java.io.Serialization
      HashMap非同步,线程不安全
      Key, value均可以为null
    • HashMap构造函数
      HashMap构造函数同Hashtable, 也有initialCapacity和loadFactor
    • HashMap本质是一个链表数组,主要包含:
      • table , 链表数组Entry[], Entry为单向链表, 有next指向下一个Entry, int hash, key-value
        在这里插入图片描述
    • HashMap源码分析
    package java.util;
    import java.io.*;
    public class HashMap<K,V> extends AbstraactMap<K,V>, implements Map, Cloneable, Serializable{
    // 默认初始容量为16,必须为2的幂
    static final int  DEFAULT_INITIAL_CAPACITY = 16;
    //最大容量必须是2的幂,且小于2的30次方,传入容量过大会被这个值替换
    static final int MAXIMOM_CAPACITY = 1<<30;
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    //存储Entry的数组,长度是2的幂
    transient Entry[] table;
    //HashMap的大小,指存储键值对的数目
    transient int size;
    //HashMap阈值,用于判断是否需要调整容量 Threshold =Capacity*LoadFactor
    int threshold;
    final float loadFactor;
    // HashMao被改变的次数
    transient volatie int modCount;
    //获取key对应的value
    public V get(Object key){
    	if(key==null){
    		return getForNullKey();
    	}
    	//获取key的hash值
    	int hash = hash(key.hashCode());
    	//在该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.equals(k))){
    			return e.value;
    		}
    		return null;
    	}
    	private V getForNullKey(){
    		for(Entry<K,V> e = table[0]; e!= null; e= e.e=next){
    			if(e.key = null;
    				return e.value;
    			}
    			return null;
    	}
    	//返回键为key的键值对
    	final Entry<K,V> getEntry(Object key){
    		int hash = (key==null ? 0: hash(key.hashCode());
    		for(Entry<K,V> e = table[indexFor(hash,table.length)]; e!= null; e = e.next)}
    			Object k;
    			if(e.hash == hash && ((key=e.key) == key || (key != null&& key.equals(k)))){
    				return e;
    			}
    		return null;
    	}
    	//containsKey()
    	public boolean containsKey(Object key){
    		return getEntry(key) != null;
    		}
    	//所以containsKey()首先通过getEntry()获取key对应的Entry,然后判读该Entry是否为Null
    	final Entry<K,V> getEntry(Object key){
    		int hash = (key==null) ? 0: hash(key.hashCode());
    		for(Entry<K,V> e = table[indexFor(hash, table.length)];  e!= null; e.next){
    			Object k;
    			if(e.hash = hash &&
    				( (k = e.key) ==key || ( key != null && key.equals(k))))
    				return e;
    			}
    		return null;
    }
    //containsValue()
    public boolean containsValue(Object value){
    	if(value ==null){
    		return containsNullValue();
    	}
    	Entry[] tab = table;
    	for(int i=0; i< tab.length; i++){
    		for(Entry e = tab[i]; e!= null; e= e.next){
    			if(value.equals(e.value){
    				return true;
    			}
    		}
    		return false;
    }
    // entrySet, values(), keySet()
    public Set<Map.Entry<K,V>> entrySet(){
    	return entrySet0();
    }
    private Set<Map.Entry<K,V>> entrySet0(){
    	set<Map.Entry<K,V>> es = entrySet;
    	return es != null ? es (entrySet = new EntrySet());
    }
    private final class entrySet extends AbstractSet<Map.Entry<K,V>> {
    	public Iterator<Map.Entry<K,V>> iterator(){
    		return newEntryIterator();
    	}
    	public boolean contains(Object o){	
    		if( o!= instanceof Map.Entry)){
    			return false;
    		}
    		Map.Entry<K,V> candidate = getEntry(e.getKey());
    		return candidate ! = null && candidate.equals(e); 
    	}
    
  • HashSet
    • 内部采用HashMap存储数据。 JDK 7采用数组+链表的形式,每一个链表为一个桶(Bucket). put元素时,首先计算该对象的HashCode,然后与bucket 总数取余, 即为对象在哈希表中的索引。当桶被占满时,就会发生hash collision, 这时链表长度很长,新插入对象又需要遍历桶中所有元素,程序执行耗时增加。为此,JDK 8引入数组+链表+红黑树 实现。当桶中对象数量超过8个,链表会自动转变为
      红黑树。
      注意这里的桶表示每一个Entry链表的数目
      HashCode可以是任意整数,包括正数或负数
    • 元素桶位置索引的计算:
    // 扰动计算: 将元素的哈希值h与h的无符号右移16位进行异或运算,得到一个新的hash值
    static final int hash(Object key){
    	int h;
    	return (key== null) ? 0 : (h = key.hashCode()) ^ (h>>>16);
    }
    
      - HashSet特点
      	- 1. 底层采用HashMap实现
      	- 2. 元素不可重复
      	- 3. 排列无序
      	- 4. 存取速度快,但是线程不安全  
    
  1. TreeSet
    TreeSet与HashSet很类似, 树集是有序集合,元素以一定的顺序添加到集合中,在元素遍历时,迭代器以排列好的顺序访问每个元素。HashMap对键进行散列,TreeMap根据键的整体顺序对元素进行排序。与键关联的值不能进行散列或比较
    TreeSet跟HashSet一样,底层实现为TreeMap.
  • TreeMap介绍
    • TreeMap是有序映射,保存键值对,底层通过红黑树实现
    • extends AbstractMap
    • implements NavigableMap<K,V> ,Cloneable, Serializable
    • TreeMap基于Red-Black tree实现,根据其键的自然顺序排序,或者根据创建映射时提供的Comparator进行排序,具体取决于使用时的构造方法
      在这里插入图片描述
    • TreeMap本质是红黑树,包含root size comparator,root 是红黑树的根节点, Entry类型,包含6个基本组成成分: key、value、left、right、parent、color。Entry节点根据key进行排序,Entry节点包含的内容为value。size是红黑树的节点数。
    • TreeMap的红黑树相关内容
      • 1 数据结构
        • 1.1 红黑树的节点颜色–红色
          private static final boolean RED = false
        • 1.2 红黑树的节点颜色–黑色
          private static final boolean BLACK = true
        • 1.3 红黑树节点对应的类
          static final class Entry<K,V> implements Map.Entry<K,V> {...}
          节点根据key进行排序,包含的内容为value.
      • 2 相关操作
        • 2.1 左旋
          private void rotateLeft(Entry<K,V> p) { ... }
        • 2.2 右旋
          private void rotateRight(Entry<K,V> p) {...}
        • 2.3 插入操作
          public V put(K key, V value)
        • 2.4 插入修正操作
          private void fixAfterInsertion(Entry<K,V> x)
        • 2.5 删除操作
          private void deleteEntry(Entry<K,V> p) {...}
        • 2.6 删除修正操作,保证红黑树删除之后仍为一颗红黑树
          private void fixAfterDeletion(Entry<K,V> x) { .. }
      • 3 TreeMap构造函数
        • 3.1 默认构造函数
          使用java默认的比较器比较Key大小,从而对其进行排序
          pubic TreeMap(){ ... }
        • 3.2 带比较器的构造器
          public TreeMap(Comparator<? super K> comparator) { this.comparator = comprarator; }
        • 3.3 带Map的构造器,Map会成为TreeMap的子集
          public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); }
          putAll将m中的所有元素添加到TreeMap中,putAll源码:
          public TreeMap(Map< ? extends K, ? extends V> m) {
          	for(Map.Entry<? extends K, ? extends V> e: m.entrySet()){
          		put(e.getKey(), e.getValue());
          }
        }	
        
        • 3.4 带sortedMap构造器
         public TreeMap(SortedMap<K, ? extends V> m) {
         	comparator = m.comparator();
         	try{
         		buildFromSorted(m.size(), m.entrySet().iterator(),null,null);
         	}catch(java.io.IOException 
         	cannotHappen){
         	}catch(ClassNotFoundException
         	cannotHappen){
         	}
         }
        
      • 4 TreeMap的Entry相关函数
        TreeMap Entry相关函数有: firstEntry() lastEntry() lowerEntry() higherEntry() floorEntry() ceilingEntry() pollFirstEntry() pollLastEntry()
      //firstEntry()源码
      public Map.Entry<K,V> firstEntry(){
      	return exportEntry(getFirstEntry());
      }
      final Entry<K,V> getFirstEntry(){
      	Entry<K,V> p = root;
      	if(p != null){
      		while( p.left != null) {
      			p = p.lleft;
      		}
      		return p;
      	}
      }
      
    • 5 TreeMap的key相关函数
      firstKey() lastKey() lowerKey() higherKey() floorKey() ceilingKey()
    //ceilingKey()源码, 返回大于等于key的最小的键值对所对应的KEY,没有的话返回Null
    public K ceilingKey( K key){
    	return keyOrNull(getCeilingEntry(key));
    }
    static <K,V>  K keyOrNull(TreeMap.Entry<K,V> E){
    	return e == null ? null: e.key;
    }
    // getCeilingKey(K key) 作用是获取TreeMap中大于/等于Key的最小节点,若不存在就返回Null
    
    
  • TreeMap的遍历
    • 1 遍历TreeMap的键值对
    //假设map是TreeMap对象
    Integer value = null;
    Iterator iter = map.entrySet().iterator();
    while(iter.hasNext()){
    	Map.Entry<K,V> entry = (Map.Entry<K,V>) iter.next();
    	key = (String) entry.getKey();
    	value = (Integer) entry.getValue();
    }
    
  • TreeSet介绍
    public class TreeSet<E>
    extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, Serializable
    源码解析
public class TreeSet<E> extends AbstractSet<E> implements Navigable<E>, Cloneable, java.io.Serializable {
	// NavigableMap对象
	private transient NavigableMap<E, Object> m;
	// TreeSet是通过TreeMap实现的
	// PRESENT是键值对中的值
	private static final Object PRESENT = new Object();
	//返回将元素按照升序排列的迭代器
	public Iterator<E> iterator() {
		return m.navigableKeySet().iterator();
	}
	//返回按照降序排列
	public Iterator<E> descendingIterator() {
		return m.descendingKeySet().iterator();
	}
  • TreeSet的遍历
//1 Iterator顺序遍历
for(Iterator iter = set.iterator(); iter.hasNext(); ) {
	iter.next();
}
//2 j降序遍历
for(Iterator iter = set.descendingIterator(); iter.hasNext(); ){
	iter.next();
}
//3 for-each遍历
String[] arr = (String[]) set.toArray(new String[0]);
for(String str :arr) {
	System.out.println("for each : %s\n", str);
}

用HashSet/HashMap遍历元素时,无法保证顺序。
这时就需要LinkedHashSet/LinkedHashMap,虽然以牺牲空间、时间为代价,但是可以通过维护运行于所有条目的双向链表,保证元素的迭代顺序。这顺序可以为插入顺序或访问顺序

  • LinkedHashMap
    public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
    LinkedHasnMap是HashMap子类,HashMap的所有非private方法均可以使用。由于是双向链表,有额外的属性:
// The head of the doubly linked list
private transient Entry<K,V> header;
// the iterator ordering method ,true表示access order 访问顺序,false表示插入顺序
private final boolean accessOrder;

数据结构上,LinkedList有两个特殊的属性: Entry<K,V> before/after, 用于维护Entry插入的先后顺序, 注意与Entry<K,V> next的区别, next维护HashMap指定table上连接的Entry顺序;双向链表的头部存放最久访问或最先插入的节点,尾部为最近访问或最近插入的节点,迭代器遍历是从链表头部开始到链表尾部结束,链表头部有个空Header节点,该节点不存放键值对,是LinkedHashMap类的成员属性,循环双向列表的入口。
LinkedHashMap初始化调用的是HashMap的初始化方法,重写了init()方法:

void init() {
	header = new Entry<K,V> (-1,null,null,null);  //依次为HashCode, key, value, next
	header.before = header.after = header; //维持双向循环列表

==以上代码中的header,是用来表示开始和结束元素,目的是记录第一个插入元素,方便遍历时找到第一个元素。

  • LinkedHashMap元素的存储
    LinkedHashMap并未重写整个put方法,而是重写了put方法调用的子方法,void recordAccess(HashMap m) void addEntry(int hash, K key, V value, int bucketIndex) void creadEntry()提供了特有的双向链表的实现
    put
public V put(K key, V value) {
	if( key == null) {
		return putForNullKey(value);
	//j计算key的hash之,进而确定在数组Table中的位置
	int hash = hash(key.hashCode());
	int i = indexFor(hash, table.length);
	//查看桶中是否有相同的key, 如果没有,直接插入,如果有,替换旧值,并返回旧值。
	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.recordValue(this);
			return oldValue;
		}
	}
	modCount++;
	//多态,重写了addEntry方法
	addEntry(hash, key, value, i);
	return null;
  }
  void addEntry(int hash, K key, V value, int bucketIndex){
  	//调用creat方法,将新元素以双向链表形式加入映射
  	creatEntry(hash, key ,value, bucketIndex);
  	// remove eldest entry if instructed, else grow capacity if appreciate
  	Entry<K,V>  eldest = header.after;
  	if(removeEldestEntry(eldest)){
  		removeEntryForKey(eldest.key);
  	}else {
  		if(size >= threshold){
  			resize(2* table.length);
  		}
  	}
 void creatEntry(int hash, K key, V value, int buketIndex){
 	HashMap.Entry<K,V> old = table[bucketIndex];
 	Entry<K,V> e= new Entry<K,V> (hash, key, value, old);
 	table[bucketIndex] = e;
 	//将该节点插入表表尾部, 让新Entry和原链表生成一个双向链表
 	e.addBefore(header);
 	size++;
 }
 private void addBefore(Entry<K,V> existingEntry){
 	after = existingEntry;
 	before = existingEntry.before;
 	before. after = this.
 	after.before = this;
 }

所以,creatEntry方法并不会拓展table大小,该方法保留table中bucketIndex处的节点,调用Entry构造方法添加一个节点,即将当前节点的Next引用指向Table[bucketIndex]的节点,之后调用e.addBefore(header)修改链表,将e节点添加到header节点之前
总之, LinkedHashMap实现就是HashMap+LinkedList方式,以HashMap维护护具结构, 以LinkedList维护插入顺序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值