转载https://feelschaotic.gitbook.io/android-knowledge-system/liu-ji-chu-neng-li/java-gao-ji/ji-he
集合
集合那么多,怎么掌握
想要掌握集合,必须先知道,为什么会有集合,集合的诞生背景是什么呢?
想想如果用数组:
- 长度固定,满了需要自己copy扩容
- 需要自己记录实际存储元素,毕竟length只拿到了array的容量
- 只能存放同一类型
- 数组不是面向对象
用起来是不是不那么顺手?集合就是在这种情况下应运而生。
不同的集合要解决的问题不一样,这么多类,别死记硬背,根据场景+类前后缀来区别。
- 想要映射关系?一律找Map后缀,映射关系的元素需不需要有序?有序是怎么个有序法?按插入顺序还是访问顺序?那我们可以在Map分类下找LinkedHashMap或TreeMap.
- 只存Value又要有序?使用List集合容器!
- 去重?使用Set集合!想要去重又有序?TreeSet!
集合是为数据和业务服务的,对外就是增、删、改、查,对内包含初始化、数据的维护、动态扩容等等细节.在学习集合时,我们应该保持一个清晰的主线
只关注几个重要的问题:
- 它解决了什么数据问题?
- 它如何解决这些问题?
- 为了解决这些问题,引入了什么新的问题,它是如何平衡的?
程序的世界里,没有银弹,否则其他集合就没有了存在的必要。也就是说每个集合一定有不同的侧重点,它在时间和空间上,一定是有所取舍,有所平衡,在这个目标下,做出最合适的优化实现。
HashMap
关键词
-
共同:二次Hash、2倍扩容、Fail-Fast机制、链地址法解决冲突、Key尽量是不可变对象、容量必须是2的n次方
-
Java7:数组+链表、链表头插(ReHash并发链表环)
-
Java8:数组+链表+红黑树、链表尾插
前言
HashMap并不简单,涉及的知识遍及线程、Java内存模型、哈希计算、链表结构、二进制操作等等,所以一个HashMap的掌握程度也能看出开发的技术功能。
那怎么学?我建议是细节吃透+发散思维。
- 咬死细节,多问几个为什么。 跟着下面几篇博文,先对HashMap的整体流程有所熟悉,再死扣设计细节。比如为什么要数组+链表这个结构?换成为什么要二次哈希?为什么String的hash算法使用固定常量31?为什么JDK1.8后使用红黑树代替过长的单链表?……
- 发散思维,多想还有什么用途。 不满足于掌握HashMap,而是要吃透知识点,那,既然涉及到哈希计算,我们平时常用的哈希算法有哪些?又有哪些应用?既然有多线程问题,那HashTable和CurrentHashMap是怎么解决的?有什么不同?既然有红黑树,红黑树在哪些地方还有应用?……
这些问题,相信阅读完下面几篇优秀的博文,你就会得到答案。
博文
-
就着配图,HashMap的源码读起来非常轻松,本文基于JDK1.7源码,非常细致地剥完了HashMap的原理。
-
从面试的角度切入,可以作为细节点的补充。
细节
-
从数学的角度论证为什么当 lenth = 2n 时,X % length = X & (length - 1)
LinkedHashMap
前言
一谈到 LRU
就离不开 LinkedHashMap
,你想想,有哪个数据结构,既有 O(1)
的取值复杂度(不冲突的情况下),又能有序。 摸清楚 LinkedHashMap
, 你也可以自己设计一个淘汰自策略的LRU
容器了 。
由于 LinkedHashMap
继承自 HashMap
,所以在分析源码之前一定要先看看 HashMap
的源码。
顺便说一句,LinkedHashMap
利用了 Hook
方法,使得自己实现有序机制的同时又能最大程度复用 HashMap
原有的流程,是模板模式的绝佳应用。
博文
-
通俗易懂,一篇你就能搞懂 LinkedHashMap 的原理,美中不足是本文基于 jdk1.7。
HashTable
略
ConcurrentHashMap
总体来说,许多人对于ConcurrentHashMap的印象,停留在以分段锁减轻免竞争条件的并发实现,这不足以完全说明其优势。
回到并发编程领域,提高并发效率可从几方面入手:
- 减少临界区区域:这是容易理解的,在并发中,临界区是依次通过的,即临界区是串行的。
- 加速通过临界区:使临界区内的逻辑代码执行得尽可能地快,这样,线程在临界区内停留的时间更短。
- 减少竞争条件:通过去除不必要的竞争条件,或竞争不同的临界区,来减少线程间的相互影响。
ConcurrentHashMap对于并发效率的优化,也将从这几方面入手。
博文
-
从普遍的角度(源码和对外API)一一来解析 ConcurrentHashMap 的原理。
-
换了另一个切入点 —— 从优化的角度来解析 ConcurrentHashMap,到底 ConcurrentHashMap 在实现并发安全的基础上如何一步步优化以更好地支持高并发呢?且看本文。
TreeMap
提起红黑树大家都不陌生,提起 TreeMap 却少有人了解,TreeMap 底层通过红黑树(Red-Black tree)实现,也就意味着 containsKey(), get(), put(), remove() 都有着 log(n) 的时间复杂度。
另一个更重要的特性是,key 支持排序,这给应用层带来了无限的发挥空间。
比如在阅读 ARouter 路由框架的时候,我就很好奇,拦截器的自定义优先级是如何实现的呢?为啥不允许多个拦截器有相同的优先级,是怎么做到的呢?开扒源码,原来是继承了 TreeMap 实现了 UniqueKeyTreeMap,数值越小就越优先添加拦截器,所以属性值设置得越低,优先级越高。
SparseArray
关键词
双数组、引入DELETE标记删除O(1)、二分查找、避免自动装箱、满了才扩容、优化追加元素可直接追在数组末尾
前言
SparseArray
相比起HashMap
来说,源码简单得多。
我们首先要清楚 SparseArray
的诞生背景和应用场景,这将影响它的特点。我们知道,谷歌官方推荐在 Android 开发中,使用SparseArray
代替HashMap
,那反过来想想,HashMap
有什么缺点呢?
- 内部负载因子扩容,意味着 1 - 负载因子剩下的空间永远不会用来放元素,浪费空间!
- Map 的 key value 需要使用包装类型,装箱操作会创建对象,如果频繁的装箱操作会消耗许多内存。
那SparseArray
又是怎么解决这个问题的呢?一切答案尽在下面的博文中。
博文
-
[原创]面试还在问 SparseArray?记住 3 句话让你临时把佛脚抱好!
这篇文章让我想起了一种学习方式,就是压缩知识信息,提炼出关键词,再牢牢记住,这样可以减少记忆量,且以后需要用到,可以用关键字推导出来,本文就是用这种关键字的方式来开篇阐述,写作技巧值得学习!