'HashMap'

来源:互联网 发布:专门下软件的网站 编辑:程序博客网 时间:2024/06/06 11:47

空间映射


数组(Node<K,V>[] table: HashMap内部类,单向链表,实现了Map.Entry,threshold( 扩容阈值:size(HashMap中实际存在的键值对数量)大于它时即Node[] resize( 会重置元素索引位置,2倍大,Hashtable为原来的2倍+2) ) =table.length(默认容量(恰当地设置 HashMap 初始容量以减少重散列)16,Hashtable的默认为11可为任意大于0的整数(2的n次方,为合数,优化了取模和扩容,同时为了减少冲突,HashMap定位哈希桶索引时也加入了高位参与运算的过程,素数导致冲突的概率相对小于合数,Hashtable扩容后不能保证还是素数)*loadFactor(负载因子默认0.75,如果内存空间多而对时间效率要求高,可以减小其值;若空间紧张而对时间效率要求不高,可增,值可大于1):连续空间,寻址迅速,但删除或者添加元素的时候需要有较大幅度的移动;链表:空间不连续,寻址难,增删元素只需修改指针;红黑树(默认链表长于8时)

modCount记录HashMap内部结构变化(并非某个key对应的value值被覆盖)次数,主要用于迭代的快速失败


put(k,v):k的hashCode()得到其hashCode 值,再通过Hash算法的高位运算(hashCode()的高16位异或低16位:h = k.hashCode()) ^ (h >>> 16),如此table.length较小时也能考虑到高低bit都参与到Hash的计算中,只抽取重要的低位防止不良离散值(poorer hashes)static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12);  return h ^ (h >>> 7) ^ (h >>> 4);})和取模( 根据hashCode和length决定索引static int indexFor(int h, int length) {    return h & (length-1);},Hashtable(将hash值先与0x7FFFFFFF,保证hash值为正数) )定位该键值对,table[i]为空new Node。

//updating  

哈希桶数组越大,Hash结果会相对分散,协同loadFactor,反之;权衡时空成本:扩容机制()与Hash算法(Hash碰撞: 拉链法:将hashtable的结构下加上链表,如果碰撞,则增加对应的hash下的链表的元素,好处是不影响其他hash值,弊如当负载因子到达阀值,扩容走起。开放寻址法:在原hashtable上搜索未使用的空间,按照搜索的步长可分为:线性探索:一步一步查找,即比如3个hash都指向1,那么第一个放在1的位置,第二个检查2的位置,如果为空则填入,不为空则查找下一个直到遍历整个hashtable,第三个也是从1位置开始轮询,若碰撞多,时间复杂度将线性增长(聚集现象); 二次探索:按k^2次探索(-m/2=<k<=m/2,m=hashtable长度),因为是跳跃的,所以比线性要好很多(聚合现象大幅度降低),但是按2次幂是跳跃,所以不能探测所有的位置; 随机探索:跟二次探索差不多,就是探索位置改为随机,也不能保证全部都探索。再散列法:使用新hash算法(或者相同的算法选取不同的seed),在同一个hashtable上还是有碰撞的可能。建立公共溢出区:把冲突的放到一个另外的区域,只有碰撞很少的情况下才有意义,不然查找还是慢



(Weak)HashMap(AbstractMap):key (与value可为null, 不同自定义类的对象作为key,其hashCode并非不同,要同时复写equals()和hashCode()) 是不可变对象,其哈希值不会被改变. 便于查找(查询时间复杂度是O(n) = O(k * n),理想状态是O(1),最坏情况从O(N链表)到O(lb(N)红黑树)),轻量级Hashtable(Dictiionary)(用ConcurrentHashMap代),都完成了Map接口,由于非线程安全,效率上高于Hashtable, 同步(Collections.synchronizedMap)。HashMap的迭代器是fail-fast迭代器,而HashTable的enumerator迭代器不是,当有其它线程改变了HashMap的结构(增加或者移除键值对对象),会抛出ConcurrentModificationException,但迭代器的remove()方法移除元素则不会抛出ConcurrentModificationException,但这并一定,要看JVM,这也是Enumeration和Iterator的区别。

LinkedHashMap (如LruCache的应用)是HashMap一子类,保存了记录的插入顺序,Iterator遍历LinkedHashMap时先得到的记录肯定是先插入的(输入输出同序,FIFO).也可以在构造时带参按照应用次数排序。遍历会比HashMap慢,不过当HashMap容量很大,实际数据较少时,遍历可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,而HashMap的遍历(遍历顺序不确定,除非put 时 key 已按 hashcode 排序)速度与其容量有关


Collection ( Iterable接口的拓展,可foreach方式对元素遍历,iterator()获取该collection的迭代器.hasNext();Iterator.next();  结合工厂方法和内部类)): 存 V 们(index);
Map(values()返回Collection):存K-V 们。


List(栈,队列,  有序, 数组: 容量,类型等):listIterator()返回一个ListIterator接口,和Iterator接口相比,Iterator单向移动,ListIterator双向, ListIterator多了add()之类,允许添加,删除,设定元素,向前或向后遍历:
LinkedList :双向链表(通过节点直接彼此连接)增删效率高,随机访问元素时效率较ArrayList低;它允许所有元素(包括null),unsynchronized,构造一个同步的List:List list = Collections.synchronizedList(new LinkedList(...));
ArrayList:线性表,顺式存储,容量,便于查询;可变大小的数组,它允许所有元素(包括null),unsynchronized


HashSet(集合): Set不许存在重复元素(集), 把存储的值作为 key,内部使用 HashMap;

TreeMap(实现SortMap)是一个有序的二叉树,用Iterator 遍历TreeMap得到的记录是排过序的。TreeSet(继承AbstractSet,实现NavigableSet(扩展的 SortedSet,能把它保存的记录根据键排序(默认是按键值的升序排),也可自定义排序的比较器)、Cloneable、Serializable接口)也是有序的. 



BlockingQueue(线程池任务队列的应用):不接受null元素,其实现(线程安全)主要用于生产者-使用者队列,可限定容量:LinkedBlockingQueue (构造函数可以规定大小,若不,所生成的BlockingQueue的大小由Integer.MAX_VALUE决定,查询时队列输出,似PriorityBlockingQueue(依据对象的自然顺序或构造函数的Comparator决定的顺序) :基于链表的队列吞吐量通常高于ArrayBlockingQueue,但如线程池线程数量很大时其性能的可预见性低于ArrayBlockingQueue(有界缓存区,其构造函数必须带一个int参数来指明其大小,所含的对象是队列顺序);SynchronousQueue(特殊的BlockingQueue,对其的操作必须是放和取交替完成 ):'空'集合,不允许 null 元素,不保存元素,直接新建,插入须等另一个元素被移除,否则一直阻塞,吞吐量常高于LinkedBlockingQueue


0 0