美文网首页
数据结构整理

数据结构整理

作者: hom03 | 来源:发表于2021-02-02 00:02 被阅读0次


HashMap

根据key获取哈希桶数组索引位置

这里的Hash算法本质上就是三步:取key的hashCode值、高位运算、取模运算。

通过h & (table.length -1)来得到该对象的保存位,而HashMap底层数组的长度总是2的n次方,这是HashMap在速度上的优化。当length总是2的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。

在JDK1.8的实现中,优化了高位运算的算法,通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度、功效、质量来考虑的,这么做可以在数组table的length比较小的时候,也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销。

扩容过程

1.初始化一个新的Entry数组,2.将数据转移到新的Entry数组里,3.HashMap的table属性引用新的Entry数组,4.修改阈值

JDK1.7扩容需要重新计算hash,jdk1.8做了优化,不需要重新计算(因为扩容都是*2,n-1就是在最高位变为1,只需要判断最高位是否为1就行)。

put方法的详细执行

HashMap JDK1.7和1.8区别

jdk1.8在扩容的新数组位置计算上做了优化,扩容都是*2,在二进制最高位变为1,所以只需要判断原来数组的最高位是否为1,为0九继续保持原坐标,避免了像1.7那样重新计算所有key的hash值的新数组坐标。

jdk1.8引入了红黑树,在key分布极不均匀情况下查询性能极大的提升,从o(n)提升到了o(lg(n)).

jdk1.8优化了hash算法提高性能减少hash冲突,计算hash值的时候,1.7用了9次扰动处理=4次位运算+5次异或,1.8只用了2次扰动处理=1次位运算+1次异或。

JDK1.7先进行扩容后进行插入,而在JDK1.8的时候则是先插入后进行扩容,提升了性能,减少不必要的开销。

发生冲突时,jdk1.7使用链地址法+头插法,jdk1.8使用链地址法+尾插法(>8使用红黑树)

Jdk1.8为何引入了红黑树

元素较少时使用链表性能更好,因为红黑树需要进行左旋,右旋操作, 而单链表不需要。

HashMap使用优化

(1) 扩容是一个特别耗性能的操作,所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。

(2) 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。

(3) HashMap是线程不安全的,不要在并发的环境中同时操作HashMap,建议使用ConcurrentHashMap。

(4) JDK1.8引入红黑树大程度优化了HashMap的性能。

(5) 还没升级JDK1.8的,现在开始升级吧。HashMap的性能提升仅仅是JDK1.8的冰山一角。


ConcurrentHashMap的底层原理

HashMap在高并发下会出现链表环,从而导致程序出现死循环。高并发下避免 HashMap 出问题的方法有两种,一是使用 HashTable,二是使用 Collections.syncronizedMap。但是这两种方法的性能都能差。因为这两个在执行读写操作时都是将整个集合加锁,导致多个线程无法同时读写集合。高并发下的 HashMap 出现的问题就需要 ConcurrentHashMap 来解决了。

jdk1.7,ConcurrentHashMap使用Segment分段锁技术,每一个 Segment 就好比一个自治区,读写操作高度自治,Segment 之间互不影响。

任何读操作都可以不用锁判断,不同Segment间的写操作也可以并发,只有同一Segment下的写操作才需要锁判断。

调用 Size() 是统计 ConcurrentHashMap 的总元素数量,需要把各个 Segment 内部的元素数量汇总起来。但是,如果在统计 Segment 元素数量的过程中,已统计过的 Segment 瞬间插入新的元素则会把各Segment加锁进行重新统计。这种思想和乐观锁悲观锁思想如出一辙,先假设 Size 过程中不会有修改,如果有修改进行悲观锁操作。

参考文章:https://www.jianshu.com/p/78989cd553b4


ConcurrentHashMap 1.7和1.8的区别

1.整体结构

1.7:Segment + HashEntry + CAS

1.8: 移除 Segment,使锁的粒度更小,Synchronized + CAS + Node

2.put操作

1.7:先定位 Segment,再定位桶,put 全程加锁,没有获取锁的线程提前找桶的位置,并最多自旋 64 次获取锁,超过则挂起。

1.8:由于移除了 Segment,类似HashMap,可以直接定位到桶,拿到 first 节点后进行判断:①为空则CAS插入;②为 -1 则说明在扩容,则跟着一起扩容;③ else 则加锁 put(类似1.7)

3.get操作

基本类似,由于 value 声明为volatile,保证了修改的可见性,因此不需要加锁。

4.resize操作

1.7:跟HashMap步骤一样,只不过是搬到单线程中执行,避免了HashMap在 1.7 中扩容时死循环的问题,保证线程安全。

1.8:支持并发扩容,HashMap扩容在1.8中由头插改为尾插(为了避免死循环问题),ConcurrentHashmap 也是,迁移也是从尾部开始,扩容前在桶的头部放置一个 hash 值为 -1 的节点,这样别的线程访问时就能判断是否该桶已经被其他线程处理过了。

5.size操作

1.7:很经典的思路:计算两次,如果不变则返回计算结果,若不一致,则锁住所有的 Segment 求和。

1.8:用 baseCount 来存储当前的节点个数,这就设计到 baseCount 并发环境下修改的问题。

linkedHashmap

LinkedHashMap是继承于HashMap,是基于HashMap和双向链表来实现的。

HashMap无序;LinkedHashMap有序,可分为插入顺序和访问顺序两种。

LinkedHashMap存取数据,还是跟HashMap一样使用的Entry[]的方式,双向链表只是为了保证顺序。

LinkedHashMap是线程不安全的。

linkedHashmap在Android中的应用

在Android中使用图片时,一般会用LruCacha做图片的内存缓存,它里面就是使用LinkedHashMap来实现存储的。


在非线性结构里面,树是非常非常重要的一种数据结构。基于其本身的结构优势,尤其在查找领域,应用广泛,其中又以二叉树最为重要。树的话我们这里只重点说一下二叉树。

二叉搜索树

二叉搜索树又叫二叉查找树,又叫二叉排序树。性质如下:(1) 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;(2) 若右子树不空,则右子树上所有结点的值均大于它的根结点的值;(3) 左、右子树也分别为二叉排序树;(4) 没有键值相等的结点。

平衡二叉树

平衡二叉树又叫AVL树。性质如下:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。

红黑树

类似与平衡二叉树的一种实现,通过改变元素节点时通过左旋和右旋操作达到平衡二叉树。

红黑树是有序的。红黑树的应用比较广泛,主要是用它来存储有序的数据,它的时间复杂度是O(lgn)。Java集合中的TreeSetTreeMap,Linux虚拟内存的管理,都是通过红黑树去实现的。

红黑树和平衡二叉树(AVL树)比较

红黑树的查询性能略微逊色于AVL树,因为其比AVL树会稍微不平衡最多一层,也就是说红黑树的查询性能只比相同内容的AVL树最多多一次比较,但是,红黑树在插入和删除上优于AVL树,AVL树每次插入删除会进行大量的平衡度计算,而红黑树为了维持红黑性质所做的红黑变换和旋转的开销,相较于AVL树为了维持平衡的开销要小得多。

红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单。平衡二叉树追求绝对平衡,条件比较苛刻,实现起来比较麻烦,每次插入新节点之后需要旋转的次数不能预知。

实际应用中,若搜索的次数远远大于插入和删除,那么选择AVL,如果搜索,插入删除次数几乎差不多,应该选择RB。

hash表和红黑树比较

红黑树是有序的,Hash是无序的,根据需求来选择。

红黑树占用的内存更小(仅需要为其存在的节点分配内存),而Hash事先就应该分配足够的内存存储散列表(即使有些槽可能遭弃用)。

红黑树查找和删除的时间复杂度都是O(logn),Hash查找和删除的时间复杂度都是O(1)。

B树

相关文章

  • 综述

    整理数据结构知识 用不同语言实现数据结构

  • Java数据结构算法(二)栈和队列

    本文旨作于收集整理使用!! 导航 Java数据结构算法(一)链表 Java数据结构算法(三)树 Java数据结构算...

  • 数据结构整理

    1、数据结构 数据、数据元素、数据项、数据对象 数据:是描述客观事物的符号,是计算机中可以操作的对象,是能被计算机...

  • 数据结构整理

    常用数据结构时间复杂度 数据结构新增查询/Find删除/DeleteGetByIndex数组 Array (T[...

  • 数据结构整理

    HashMap 根据key获取哈希桶数组索引位置 这里的Hash算法本质上就是三步:取key的hashCode值、...

  • 2. 常用的数据结构

    1. 数据结构:八大数据结构分类2.数据结构导读目录 3.常见的数据结构与算法整理 1. 数组 介绍:在内存中连...

  • Java 中的数据结构及原理

    最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程。 小编整理...

  • Java(Android)数据结构汇总(四)-- Map(上)

    传送门:Java(Android)数据结构汇总 -- 总纲 简介 这篇主要来整理下基于Map接口实现的数据结构类。...

  • 打卡日(7)

    2018-03-19 1. 准备自己整理下常用的数据结构和算法,用C和java整理

  • Redis内容整理(2021-06-29)

    Redis内容整理 目录 基础数据结构 持久化 主从架构 哨兵模式 集群配置 面试整理[https://www.j...

网友评论

      本文标题:数据结构整理

      本文链接:https://www.haomeiwen.com/subject/ubakzktx.html