×

hashmap底层原理

hashmap底层原理(三月底离职,到目前面试了十几家,为什么面试官总是喜欢问很底层的原理问题这些问题)

admin admin 发表于2023-03-24 07:58:34 浏览35 评论0

抢沙发发表评论

本文目录

三月底离职,到目前面试了十几家,为什么面试官总是喜欢问很底层的原理问题这些问题

题主你好,很高兴回答你的问题!

作为一名职场填坑多年,参加多次应聘求职,完成过几次招聘和岗位职责说明书的人,我谈一下对问题的看法。

面试是为了挑选有用的人,而不是挑选“高大上”

公司招聘,实际上是以实用为主的,作为面试官需要通过面试过程的信息筛选挑选出最合适的人。如果是一些基础性的岗位,那么基层的经验和原理是考察一个人岗位熟练程度的最好办法。至于说高大上的问题,更多的是测试求职者的附加价值,也就是意外惊喜,这一块作为参考条件即可。

面试只是一种方法,结果判定才是手段

面试官面试的时候,都会有自己的“套路”。作为应聘者,我们要做的就是见招拆招。一个简单的问题,同样的回答,不同的人有不同的判定,不要纠结面试官问什么,重点关注你回答了什么!

求职应聘,最重要的是表现让对方满意,至于说能否体现全部实力不重要

相中了一份工作进行面试,我们的关注焦点应该是如何通过面试。至于说对方问的什么,如何评判实际上不重要。

原理类的问题看似简单实际上很有技术含量

一些与基础经验有关系的岗位,问现场的原理性问题能看出应聘者对现场问题的了解和掌握情况!最底层的问题恰好最能体现一个人的实践经历,有没有在现场做过事,只要问一个现场小问题的处理就能看的出,这些恰好是可以排查“面霸”的最好工具。

在学校学习java感觉并没有学什么特别的技术,只是增删改查和一些框架,怎么办

作为一名IT行业的从业者,同时也出版过Java编程书籍,所以我来回答一下这问题。

首先,本科阶段学习的Java编程技术还是以基础内容为主,包括基本的Java编程语法、Web开发基础以及大数据开发基础等等。

如果要想提升自身的岗位竞争力,还需要进一步学习,可以参考以下几个方面的建议:

第一:选择一个具体的学习方向。Java目前的开发方向主要集中在Web开发领域、Android开发领域以及大数据开发领域,从目前的发展趋势来看,大数据方向是不错的选择。如果对移动互联网开发比较感兴趣,也可以选择Android开发,目前Android开发已经逐渐被并入到前端开发团队,所以选择Android开发方向需要进一步掌握各种前端开发技术。由于Java Web开发的技术体系比较成熟,所以选择Java Web开发是不错的选择。

第二:制定具体的学习计划。学习计划的制定要依赖于具体的学习方向,以大数据方向为例,学习的内容有三大部分,其一是大数据平台知识;其二是大数据平台针对于Java的API;其三是进行具体的案例开发。另外,要想从事大数据开发还需要具备一定的数学基础和统计学基础。

第三:加强动手实践能力。对于大学生来说,动手实践能力的培养对于提升就业竞争力是非常重要的,一方面要在学习的过程中完成规定的各种实验,另一方面也要通过自主学习来提升动手能力。如果在校期间能够参与到老师的课题组,或者参加各种级别的比赛,都会促进自身动手实践能力的提升。

我从事互联网行业多年,目前也在带计算机专业的研究生,主要的研究方向集中在大数据和人工智能领域,我会陆续写一些关于互联网技术方面的文章,感兴趣的朋友可以关注我,相信一定会有所收获。

如果有互联网、大数据、人工智能等方面的问题,或者是考研方面的问题,都可以在评论区留言!

Java程序员如何突破三年的门槛

工作3年了,同样是程序员,为什么别人每月28K你却只有16K,如何才能突破自己得到持续成长呢?这是每一个程序员都绕不开的话题。在这里和大家分享我从程序员进阶成为java高级工程师/架构师的一些学习方向,Java进阶之路离不开一个长期系统的学习规划,方向方法正确了,结果自然是好的。以下,enjoy~

一、常见模式与工具

1. 常用设计模式:Proxy代理模式、Factory工厂模式、Singieton单例模式等

2. Spring5:IOC容器设计原理及高级特性,AOP设计原理、FactoryBean与BeanFactory,Spring事务处理机制等

3. MyBatis:代码自动生成品,缓存使用场景及选择策略,MyBatis的事务分析MyBatis的动态代理的真正实现等

二、常用工具

1. Maven:项目管理

2. Jenkins:持续集成

3. Sonar:代码质量管理

4. Git:版本管理

三、分布式架构

1. 架构原理

2. 架构策略

3. 中间件

4. 架构实战

四、微服务架构

1. 微服务框架

2. Spring Cloud

3. Docker与虚拟化

4. 微服务架构

五、性能优化

1. 性能指标体系

2. JVM调优

3. Tomcat调优

4. MySQL调优

六、底层知识

1. 内存模型

2. 并发模式

3. 线程模型

4. 锁细节

以上,只是列举一个大概的学习方向,工作几年,走着走着,我们就会发现,身边总有些程序员成长得特别快,对此,不能一叶障目,只见他人加薪晋级,却看不见他人工作之余对学习的坚持不懈。人生机会并不多,当下努力,以后才能有更多自由与选择。以下福利,送给希望进阶成为架构师的你,助力进阶加薪~

【福利】由BAT背景架构师原创出品的java架构师学习80期专题资料合集,私信关键词【架构】给优知学院,立即免费秒领。

都划到这儿了,点个赞呗!

都划到这儿了,点个赞呗!

java自学成功入职一年,现阶段学习哪些知识,提高自身技术水平

首先,恭喜你,能够通过自学Java进入互联网行业。你现在已经入职一周年,现在基本的 Java 语法使用,你应该都已经掌握,可能会有很多东西你可以通过百度或者谷歌能够搜索出来,能够很快的满足业务类的需求开发。既然你作为 Java 开发程序员,那么接下来你就需要对 Java 方面的技术知识,要有更深入的学习和使用。

接下来对于 Java 的学习,我建议你先从 Java 集合类学习入手,现在我平时写代码的时候,使用 Java 集合类的地方非常的多。Java 集合总体上可以分为:List(数组)、Set(去重集合)、Map(映射)、队列,在进行细分的话,有 ArrayList、HashSet、HashMap等等。

你需要了解到集合类的使用,同时,集合类底层的源码到底是怎么实现的,现在面试时问的最多的,比如 HashMap 底层的实现,以及 HashMap 扩容时需要注意什么。Java集合类,使用固然重要,但是知道其底层的原理实现,能够让你更好的去使用它们,同时,未来跳槽时,应对互联网大厂面试,也是很有必要的。

Java 线程以及Java虚拟机方面,建议在对 Java 的语法以及集合类熟悉之后,在进行学习。这部分知识说实话,如果不经常代码实践的话,可能看完过一段时间,就会忘记。Java 虚拟机方面最重要的,还是要懂得 Java 堆的划分,垃圾回收的算法,以及对于 Java 堆内存进行调优。调优主要是要掌握不同内存代的垃圾回收算法的特点,以及相关 Java 参数的设置。

对于 Java 语言有了很深入的了解之后,下一步就是去熟悉 Java 技术框架的使用和原理。比如 Spring、Spring Boot的学习,同时还有网络方面的知识,TCP 以及 UDP的区别。总之,Java 技术栈非常的广,你可以确定好自己未来的职业发展之后,在深入的学习你职业相关技术栈的原理。

结语

我是Lake,专注大数据技术原理、人工智能、数据库技术、程序员经验、编程语言分享,如果我的问答对你有帮助的话,希望你能点赞关注我,感谢。

我会持续分享在科技方面的内容,如果你有任何问题,也欢迎关注私信我,我会认真解答每一个问题,期待您的关注。

学习Java到什么程度可以找到工作

想要求职找到工作,最好的方法就是跟着企业的招聘需求走。因为企业的需求会随着时间变化而变化,你可以多去看看招聘网站上企业的需求,这样就能根据你的期望薪酬判断自己的Java应该学到哪种程度。

以上是在招聘网站上截取的三个不同薪资水平的Java岗位,可以明显看到岗位要求各不相同。所以想知道自己的Java需要学到什么程度,取决于你想获得什么层次的薪资水平。

盲猜题主可能是自学的,那就分享起薪在5K左右的Java实习生的标准给你参考:

1、熟练掌握JavaSE编程基础。

2、熟悉JavaEE开发技术JSP、Servlet、JDBC、Ajax等技术。
3、前端方面:熟悉HTML、XML、CSS、Javascript(不需要太深入,能写出需求页面就行)、jQuery、Bootstrap等技术。

4、工具:至少掌握Eclipse、IDEA中一种开发工具。

5、数据库:至少掌握MySQL、Oracle其中一种。

6、框架:熟练掌握Spring+SpringMVC+Mybatis,熟悉Struts、Hibernate(目前企业使用少一些了)等。

7、项目:最好到Github找一些开源项目完成,有项目经历面试成功几率会大一些。

如果能掌握好以上的内容,相信是可以找到一份初级的Java工作的。

已经学完JAVASE,后面学JAVA WEB JAVA安卓,JAVA架构师这个顺序合适吗

你学习完 Java SE 部分,也就是说你把 Java 语言基础部分的知识已经学习完成。我个人认为 Java Web 和 Java 安卓是两个单独的方向,所以你学习完 Java SE 后,在学习 Java Web、Java 安卓,然后再到 Java 架构师,这个顺序不合适。

如果你对 Java Web 后端开发感兴趣,你的学习方向应该是Java SE、Java Web、Java架构师,如果你对安卓开发感兴趣,你的学习方向应该是 Java SE、Java安卓、Java架构师。不要把 Java Web 和 Java 安卓一起学习,专注一个方向就好。

Java 基础方面涉及到的知识点很多,请确保你的 Java 基础足够扎实

Java 基础方面涉及到的知识点非常多,而且有很多知识点所涉及到的细节比较深入。结合我18年校招面试互联网大厂的经验,Java 集合方面、Java 多线程方面、以及 Java 虚拟机方面都是必然会被问到的,如果你想进大厂,请确保你掌握了这些知识点。

Java 集合涉及到 List、Set、Map等集合类,常见集合的底层实现原理你需要掌握,比如 ArrayList、HashSet、HashMap等,尤其是 HashMap 底层的原理实现,这个一定要完全掌握,这个几乎是 Java 面试必问的一个题目。

Java 多线程方面会被问到,比如线程锁的实现、生成者消费者模型的编写。工作当中倒是不会接触到太多。Java 虚拟机方面则是垃圾回收算法、内存的划分、虚拟机类加载机制。如果上面我说的这些存在你不会的地方,建议你对这些知识点在进行学习。

安卓现在就业机会没有以前那么多了,而且谷歌已经将 Kotlin 作为安卓开发语言,不建议你学习安卓

谷歌在19年5月8号的 I/O 开发者大会,宣布未来 Kotlin 成为安卓开发首选语言。官方都建议使用 Kotlin语言来开发安卓,而不是 Java 语言,可想而知,在未来,使用Java来开发安卓程序会变得越来越少。

结合我的个人经验,我现在觉得安卓开发的岗位其实已经没有那么多了,整体安卓开发岗位市场趋于饱和,像我现在所在的公司,招聘安卓开发的岗位很少。所以你学习完 Java 基础之后,建议你还是转向 Java Web 方向会更好。

Java Web 方向会接触到很多Java后端的技术,这对于你未来转向 Java 架构师方向,会更有帮助。

我是Lake,专注大数据技术原理、人工智能、数据库技术、程序员经验分享,如果我的问答对你有帮助的话,希望你能点赞关注我,感谢。

我会持续大数据、数据库方面的内容,如果你有任何问题,也欢迎关注私信我,我会认真解答每一个问题。期待您的关注

java面试都问知不知道hashmap的原理,那我就想问,知道原理有什么用

Java中的HashMap可以说是平时开发中最常用的数据结构之一了,经常使用的集合类还有ArrayList、HashSet,基本上用好HashMap、ArrayList、HashSet这三大集合类,大多数的业务场景就满足了,掌握这三大集合类也是作为一名Java程序员的基础能力。

平时开发大多数的业务场景都是CRUD,且数据量都很小,所以基本上不会有什么问题。那么还需要知道其底层实现原理吗?还需要知道这些集合类的数据结构吗?

当然需要,这很重要!这里就拿HashMap来具体说一说了解它的设计思想多么的重要!

HashMap的数据结构

HashMap的底层数据结构简单来说就是数组+链表+红黑树,这个大家都知道,面试也是高频面试题,用一张图来形容就是:

那这个时候你就得知道数组的好处了,基于下标的随机访问和赋值数组元素的时间复杂度都是O(1),这就能保证HashMap数据没有哈希冲突的时候它的set/put方法都是O(1)的,这也是HashMap要追求的极致目标(尽管会有哈希冲突)。这就是HashMap查询性能快、插入数据快的主要原因,是一个空间换时间的思想。

哈希

但前提是我们得知道我们要把一个数据插入到数组的哪个下标,因此就采用了哈希的思想。一个对象一定有一个唯一的hash值,但是两个对象也有可能有相同的hash值,这叫“哈希冲突”。所以为了更好的利用数组,哈希值计算要尽可能的避免冲突,也就是追求“低碰撞率”。

这也涉及到另外一个问题,比较一个对象的时候为什么要重写它的hashcode()方法和equals()方法。

那业内除了Java自带的Hashcode()方法还有哪些hash算法你了解吗?比如MurmurHash算法。他们都在哪些开源软件中应用到?各种哈希算法的性能比较又如何?我们平时开发能不能借鉴这种思想?

数组与链表

当哈希冲突的时候,HashMap就会使用到链表,即数组+链表,那你知道数组和链表的区别吗?LinkedHashMap和HashMap的区别呢?都适合在哪些场景用到?如果让你手写一个LRU缓存,你会怎么写?

你可能想说我不需要知道数组和链表的数据结构,我也没有手写LRU缓存的场景,我只想做一条安静的咸鱼,简简单单CRUD就好。

高效查找

大家都说平时开发都是CRUD,那你知道如何把CRUD写的高大上一点吗?比如其中的C(查询)应该是最为频繁的。学过数据结构的都知道,高效查找主要的两种算法:有序查找(二分)和哈希查找。HashMap的数组就是用到了哈希查找,时间复杂度是O(1),那么你理解了HashMap的原理是不是就基本掌握了哈希查找算法的原理?另外当哈希冲突导致链表节点数量达到8时候,就会变成红黑树,红黑树就是有序查找的变种。如果你又进一步掌握了红黑树的查找原理,是不是就基本掌握了有序查找算法的原理?所以HashMap的原理重不重要?掌握了HashMap的原理是不是就掌握了高效查找的方法?如果你没掌握这些原理,你觉得掌握了没有用,但是当你掌握了,在日常业务开发中你会发现受用无穷。

HashMap中还有很多思想值得大家学习,掌握这些思想后,其实才是你编程能力的质的提升。手里有武器不用和手里没有武器不是一回事

Sardine调用put方法的底层实现怎么做

hashmap put方法的实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

首先对key做null检查。如果key是null,会被存储到table,因为null的hash值总是0。

key的hashcode()方法会被调用,然后计算hash值。hash值用来找到存储Entry对象的数组的索引。有时候hash函数可能写的很不好,所以JDK的设计者添加了另一个叫做hash()的方法,它接收刚才计算的hash值作为参数。如果你想了解更多关于hash()函数的东西,可以参考:hashmap中的hash和indexFor方法

indexFor(hash,table.length)用来计算在table数组中存储Entry对象的精确的索引。

在我们的例子中已经看到,如果两个key有相同的hash值(也叫冲突),他们会以链表的形式来存储。所以,这里我们就迭代链表。

· 如果在刚才计算出来的索引位置没有元素,直接把Entry对象放在那个索引上。

· 如果索引上有元素,然后会进行迭代,一直到Entry-》next是null。当前的Entry对象变成链表的下一个节点。

· 如果我们再次放入同样的key会怎样呢?逻辑上,它应该替换老的value。事实上,它确实是这么做的。在迭代的过程中,会调用equals()方法来检查key的相等性(key.equals(k)),如果这个方法返回true,它就会用当前Entry的value来替换之前的value。

2.hashMap get方法的解析:

1

2

3

4

5

6

7

当你传递一个key从hashmap总获取value的时候:

对key进行null检查。如果key是null,table这个位置的元素将被返回。

key的hashcode()方法被调用,然后计算hash值。

indexFor(hash,table.length)用来计算要获取的Entry对象在table数组中的精确的位置,使用刚才计算的hash值。

在获取了table数组的索引之后,会迭代链表,调用equals()方法检查key的相等性,如果equals()方法返回true,get方法返回Entry对象的value,否则,返回null。

3.要牢记以下关键点:

  1. · HashMap有一个叫做Entry的内部类,它用来存储key-value对。
  2. · 上面的Entry对象是存储在一个叫做table的Entry数组中。
  3. · table的索引在逻辑上叫做“桶”(bucket),它存储了链表的第一个元素。
  4. · key的hashcode()方法用来找到Entry对象所在的桶。
  5. · 如果两个key有相同的hash值,他们会被放在table数组的同一个桶里面。
  6. · key的equals()方法用来确保key的唯一性。
  7. · value对象的equals()和hashcode()方法根本一点用也没有。

简单地说,HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时, 也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。

HashMap的resize(rehash)

当hashmap中的元素越来越多的时候,碰撞的几率也就越来越高(因为数组的长度是固定的),所以为了提高查询的效率,就要对hashmap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,所以这是一个通用的操作,很多人对它的性能表示过怀疑,不过想想我们的“均摊”原理,就释然了,而在hashmap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。 那么hashmap什么时候进行扩容呢?当hashmap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。比如说,我们有1000个元素new HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上面annegu已经说过,即使是1000,hashmap也自动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为0.75*1000 《 1000, 也就是说为了让0.75 * size 》 1000, 我们必须这样new HashMap(2048)才最合适,既考虑了&的问题,也避免了resize的问题。

hashmap和concurrenthashmap的区别,hashmap的底层源码


你好。 有并发访问的时候用ConcurrentHashMap,效率比用锁的HashMap好 功能上可以,但是毕竟ConcurrentHashMap这种数据结构要复杂些,如果能保证只在单一线程下读写,不会发生并发的读写,那么就可以试用HashMap。ConcurrentHashMap读不加锁,写...

java8 中concurrenthashmap数据结构和HashMap一样,且线程安全 为什么还要HashMap


java阻塞队列应用于生产者消费者模式、消息传递、并行任务执行和相关并发设计的大多数常见使用上下文。

BlockingQueue在Queue接口基础上提供了额外的两种类型的操作,分别是获取元素时等待队列变为非空和添加元素时等待空间变为可用。

BlockingQueue新增操作的四种形式:

请点击输入图片描述

插入操作是指向队列中添加一个元素,至于元素存放的位置与具体队列的实现有关。移除操作将会移除队列的头部元素,并将这个移除的元素作为返回值反馈给调用者。检查操作是指返回队列的头元素给调用者,队列不对这个头元素进行删除处理。

抛出异常形式的操作,在队列已满的情况下,调用add方法将会抛出IllegalStateException异常。如果调用remove方法时,队列已经为空,则抛出一个NoSuchElementException异常。(实际上,remove方法还可以附带一个参数,用来删除队列中的指定元素,如果这个元素不存在,也会抛出NoSuchElementException异常)。如果调用element检查头元素,队列为空时,将会抛出NoSuchElementException异常。

特殊值操作与抛出异常不同,在出错的时候,返回一个空指针,而不会抛出异常。

阻塞形式的操作,调用put方法时,如果队列已满,则调用线程阻塞等待其它线程从队列中取出元素。调用take方法时,如果阻塞队列已经为空,则调用线程阻塞等待其它线程向队列添加新元素。

超时形式操作,在阻塞的基础上添加一个超时限制,如果等待时间超过指定值,抛出InterruptedException。

阻塞队列实现了Queue接口,而Queue接口实现了Collection接口,因此BlockingQueue也提供了remove(e)操作,即从队列中移除任意指定元素,但是这个操作往往不会按预期那样高效的执行,所以应当尽量少的使用这种操作。

阻塞队列与并发队列(例如ConcurrentLinkQueue)都是线程安全的,但使用的场合不同。

Graphic3-1给出了阻塞队列的接口方法,Graphic3-2给出了阻塞队列的实现类结构。

Graphic 3-1 BlockingQueue接口

Graphic3-2阻塞队列的实现类

请点击输入图片描述

3.1.1 ArrayBlockingQueue类

一个以数组为基础的有界阻塞队列,此队列按照先进先出原则对元素进行排序。队列头部元素是队列中存在时间最长的元素,队列尾部是存在时间最短的元素,新元素将会被插入到队列尾部。队列从头部开始获取元素。

ArrayBlockingQueue是“有界缓存区”模型的一种实现,一旦创建了这样的缓存区,就不能再改变缓冲区的大小。ArrayBlockingQueue的一个特点是,必须在创建的时候指定队列的大小。当缓冲区已满,则需要阻塞新增的插入操作,同理,当缓冲区已空需要阻塞新增的提取操作。

ArrayBlockingQueue是使用的是循环队列方法实现的,对ArrayBlockingQueue的相关操作的时间复杂度,可以参考循环队列进行分析。

3.1.2 LinkedBlockingQueue

一种通过链表实现的阻塞队列,支持先进先出。队列的头部是队列中保持时间最长的元素,队列的尾部是保持时间最短的元素。新元素插入队列的尾部。可选的容量设置可以有效防止队列过于扩张造成系统资源的过多消耗,如果不指定队列容量,队列默认使用Integer.MAX_VALUE。LinkedBlockingQueue的特定是,支持无限(理论上)容量。

3.1.3 PriorityBlockingQueue

PriorityBlockingQueue是一种基于优先级进行排队的无界队列。队列中的元素按照其自然顺序进行排列,或者根据提供的Comparator进行排序,这与构造队列时,提供的参数有关。

使用提取方法时,队列将返回头部,具有最高优先级(或最低优先级,这与排序规则有关)的元素。如果多个元素具有相同的优先级,则同等优先级间的元素获取次序无特殊说明。

优先级队列使用的是一种可扩展的数组结构,一般可以认为这个队列是无界的。当需要新添加一个元素时,如果此时数组已经被填满,优先队列将会自动扩充当前数组(一般认为是,先分配一个原数组一定倍数空间的数组,之后将原数组中的元素拷贝到新分配的数组中,释放原数组的空间)。

如果使用优先级队列的iterator变量队列时,不保证遍历次序按照优先级大小进行。因为优先级队列使用的是堆结构。如果需要按照次序遍历需要使用Arrays.sort(pq.toArray())。关于堆结构的相关算法,请查考数据结构相关的书籍。

在PriorityBlockingQueue的实现过程中聚合了PriorityQueue的一个实例,并且优先队列的操作完全依赖与PriorityQueue的实现。在PriorityQueue中使用了一个一维数组来存储相关的元素信息。一维数组使用最小堆算法进行元素添加。

Graphic3-3PriorityBlockingQueue的类关系

      

请点击输入图片描述

3.1.4 DelayQueue

一个无界阻塞队列,只有在延时期满时才能从中提取元素。如果没有元素到达延时期,则没有头元素。

3.2 并发集合

在多线程程序中使用的集合类,与普通程序中使用的集合类是不同的。因为有可能多个线程同时访问或修改同一集合,如果使用普通集合,很可能造成相应操作出现差错,甚至崩溃。Java提供了用于线程访问安全的集合。(前面讨论的BlockingQueue也是这里集合中的一种)。下面针对这些集合,以及集合中使用的相应算法进行探讨。在设计算法时,仅对相应算法进行简要说明,如果读者需要深入了解这些算法的原理,请参考其他的高级数据结构相关的书籍。

3.2.1 ConcurrentMap接口

ConcurrentMap接口在Map接口的基础上提供了一种线程安全的方法访问机制。ConcurrentMap接口额外提供了多线程使用的四个方法,这四个方法实际是对Map已有方法的一个组合,并对这种组合提供一种原子操作。Graphic3-4给出了ConcurrentMap相关的操作。Graphic3-5给出了ConcurrentMap的实现类关系图。

从Graphic3-5中可以看出ConcurrentNavigableMap继承自ConcurrentMap,ConcurrentNavigableMap是一种SortedMap,就是说,映射中的元素会根据键值进行排序的。在java.util类库中,有两个类实现了SortedMap接口,分别是TreeMap和ConcurrentSkipListMap。TreeMap使用的是红黑树结构。而ConcurrentSkipListMap使用作为底层实现的SkipList(翻译为跳表)数据结构。此外ConcurrentHashMap实现了ConcurrentMap接口,使用的是HashMap方法。

Graphic3-4 ConcurrentMap

Graphic3-5 实现ConcurrentMap接口。

请点击输入图片描述

3.2.1.1 TreeMap

尽管TreeMap不是线程安全的,但是基于其数据结构的复杂性和方便对比说明,还是在这里简单提一下。TreeMap实现了SortedMap接口。TreeMap使用的是红黑树(这是高等数据结构中的一种),在红黑树算法中,当添加或删除节点时,需要进行旋转调整树的高度。使用红黑树算法具有较好的操作特性,插入、删除、查找都能在O(log(n))时间内完成。红黑树理论和实现是很复杂的,但可以带来较高的效率,因此在许多场合也得到了广泛使用。红黑树的一个缺陷在于,可变操作很可能影响到整棵树的结构,针对修改的局部效果不好。相关算法请参考