【问答】Java集合

用Hashmap实现redis有什么问题

Redis的缓存可以持久化,Map是内存对象,程序一重启数据就没了;Redis可以实现分布式的缓存,Map只能存在创建它的程序里;Redis缓存有过期机制,Map本身无此功能;Redis可以用几十G内存来做缓存,Map不行,一般JVM也就分几个G数据就够大了;hashmap不是线程安全的,可以考虑concurrentHashmap

遍历Hashmap的三种方式

for (int key : hashmap.keySet()); while (Iterator.hasNext()); for(Map.Entry<Integer, String> entry : hashmap.entrySet()); for (String value : hashmap.values())

Hashmap如果只有一个写其他全读会出什么问题

单线程写多线程读时,get操作可能得到null值:写线程如果触发了扩容,扩容过程中会先新建临时空数组再赋给成员数组(真正放KeyValue的),此时成员数组没有任何元素,再循环根据元素的hash放入空的成员数组的对应的桶位置,过程中度线程获取元素可能为null;

多线程写多线程读时,可能会导致get操作时死循环(JDK1.8已解决)、put非null元素后get操作得到null值、元素丢失3个问题,都是多线程写触发扩容resize,重写分配元素桶和链表上的位置导致的;

JDK1.8之前并发操作Hashmap时为什么会有死循环的问题?

JDK1.7采用数组+链表实现,put元素时若元素个数超过阈值会触发扩容,扩容是原数组的2倍,执行resize方法,resize会调用transfer方法转移老数组中的元素,转移后的链表是倒序的(如果在新表的数组索引位置相同时),多线程转移过程中设置e.next很容易形成环,get操作时就形成死循环;

JDK1.8采用数组+链表+红黑树实现,元素是在链表末端进行添加,链表顺序不会变,因此不会形成环,就没有死循环的问题,尽管如此HashMap任是不安全的; 

代替HashMap:Hashtable,ConcurrentHashMap,Collections.synchronizedMap;

Hashmap内部的数据结构是什么?底层是怎么实现的?

JDK7的HashMap采用数组+链表实现,JDK8采用数组+链表+红黑树实现,数组定义Node<K, V>[] table,当链表节点大于8转换为红黑树,小于6转换为链表,链表是为了解决哈希冲突,红黑树是为了提高性能,线程不安全;

扩容:默认16,增长因子0.75,超过容量(设置的容量会转换成2n次方)*增长因子时将进行扩容,容量翻倍,HashMap要求容量必须是2的幂,resize方法循环将老元素放入新容器;

Hashmap如何解决hash冲突,为什么Hashmap中的链表需要转成红黑树?

采用拉链法解决,put元素时根据key的hashcode再hash得到key的hashcode,根据hashcode和数组长度计算得到元素存储的位置即桶的位置,如果桶为空则放到桶上,如果非空说明存在hashcode相同的元素即hash冲突,则把该元素放到链表末尾;

单链表的查询复杂度是O(n),红黑树查询复杂度是O(logn),为了提高查询效率,节点数为8时会将链表转成红黑树,为6时转成链表;选择8是由概率统计决定的,理想情况下好的hash算法可以尽可能避免hash冲突,节点分布在桶中的频率遵循泊松分布,链表中元素个数为8时概率很小,避免链表和树的频繁转换;

Hashmap什么时候会触发扩容?

当HashMap中的元素越来越多的时候,碰撞的几率也就越来越高(因为数组的长度是固定的),所以为了提高查询的效率,就要对HashMap的数组进行扩容;

当元素个数超过数组大小*0.75时就会扩容,扩容大小为原大小*2,然后重新计算每个元素所在的位置,这是扩容过程中最耗时的操作;所以已知大小时初始化时设置容量可提高性能,需要注意设置的容量和扩容因子的关系,如1000个元素,最好设置为靠近1000/0.75的2次幂2048;

Hashmap扩容时每个entry需要再计算一次hash吗?

JDK7会,JDK8不会; 由于扩容是原来的2倍即2次幂,所以元素的位置要么在原位置,要么在原位置再移动2次幂的位置,JDK8中通过使用e.hash & oldCap来计算高位和低位的hash值,来把原来在一个槽位上面的链表拆分成两个链表即可;JDK1.7中rehash的时候,旧链表迁移新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置,JDK1.8不会倒置;

Hashmap的数组长度为什么要保证是2的幂?

&运算代替%运算,若不为2的幂次,内部数组会存在浪费 ;扩容时方便定位,当相与的该位为0时,则结果不变,扩容后索引值不变,当相与的该位为1时,则结果为原索引值加原数组长度 ;

Hashmap key重复了怎么办? 是如何解决的?

put元素时先计算key的hashcode得到桶的位置,再equals查找有无相等的元素,有则直接更新,没有则放入链表;即hashcode和equals都相等才认为key相等,IdentityHashMap只要==即地址相等才认为key相同,从而可以实现放入重复的key;

Hashmap有什么漏洞会导致他变慢?

其中一个问题:在一个Key被放进HashMap和从 HashMap取出时两次计算它的hashCode不一致(中间对计算hashcode的成员变量做了修改)就会导致获取不到元素,可能导致内存泄露;另外如果Key的hashcode计算不正确,导致所有元素的hashcode落到一个桶中,导致桶很大,查询就会变慢;

如何给Hashmap的key对象设计他的hashcode?

实现hashcode时需重写equals;hashCode的结果具有一致性,即无论何时计算得到的结果都一样,这就要求计算hashCode依赖的值是不可变的;hashCode必须基于对象的内容生成;hashCode产生的散列码最好能均匀分布;

用过哪些Map类,都有什么区别,Hashmap是线程安全的吗,并发下使用的Map是什么,他们内部原理分别是什么?

HashMap:底层实现:JDK7的HashMap采用数组+链表实现,JDK8采用数组+链表+红黑树实现,当链表节点大于8转换为红黑树,小于6转换为链表,链表是为了解决哈希冲突,红黑树是为了提高性能,线程不安全;扩容:默认16,增长因子0.75,超过容量(设置的容量会转换成2n次方)*增长因子时将进行扩容,容量翻倍,HashMap要求容量必须是2的幂,resize方法循环将老元素放入新容器;允许空元素;LinkedHashMap:继承HashMap,LinkedHashMap=HashMap+双向链表维护插入或访问顺序;TreeMap:基于红黑树实现,有序(基于key和Comparable排序),遍历速度较快,线程不安全;不允许空元素;Hashtable:方法有synchronized,线程安全;IdentityHashMap:Key可重复,只要对象地址不相等;WeakHashMap:同HashMap,Entry继承弱引用,每次resize等情况会清理引用;

JAVA8的ConcurrentHashmap为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。

JDK1.7中的Segment继承了ReentrantLock,每个锁控制的是一段,当每个Segment越来越大时,锁的粒度就变得有些大了,而且Segment越多越耗内存;JDK1.8放弃了分段锁而是用了Node锁,只需要对头结点使用synchronized进行同步,减低锁的粒度,提高性能,并使用CAS操作来确保Node的一些操作的原子性; 

使用synchronized而不用ReentrantLock,一是为了减少内存开销,如果使用ReentrantLock则需要节点继承AQS来获得同步支持,增加内存开销,而1.8中只有头节点需要进行同步;二是内部优化,synchronized则是JVM直接支持的,JVM能够在运行时作出相应的优化措施:锁粗化、锁消除、锁自旋等等 

Concurrenhashmap求size是如何加锁的,如果刚求完一段后这段发生了变化该如何处理

JDK1.7中,size操作通过一种比较巧的方式避免对所有的segment加锁,Segment中的有一个modCount变量,代表的是对Segment中元素的数量造成影响的操作的次数,这个值只增不减,size操作就是遍历了两次Segment,每次记录Segment的modCount值,然后将两次的modCount进行比较,如果相同,则表示期间没有发生过写入操作,就将原先遍历的结果返回,如果不相同,则把这个过程再重复做一次,如果再不相同,则就需要将所有的Segment都锁住,然后一个一个遍历了;

 JDK1.8中,size操作比较简单,没有加锁,而是在插入删除数据时维护volatile long baseCount(元素个数)和volatile CounterCell[] counterCells(并发时更新baseCount失败的元素个数),然后累加即可; 

如何用LinkedHashmap实现LRU?

LRU最近最少使用,实现思路是维持一个链表,把刚被插入的节点放在链表尾,刚被访问的节点脱链,也放到链表尾,每次需要淘汰节点的时候就淘汰链表头;LinkedHashMap底层实现同HashMap,增加了双向链表来记录节点的插入或者访问顺序,同时节点按桶分布避免了遍历链表时的全表扫描,构造函数时设置按访问顺序链接节点,这样链表头就是最久没访问的节点,同时构造时重写removeEldestEntry方法(默认返回false,需根据缓存大小和链表长度判断,当链表长度超过缓存大小时返回true)来删除头结点,同时需对LinkedHashMap的方法使用synchronized包装保证线程安全;

LinkedHashmap基本原理

LinkedHashMap继承HashMap,增加了head/tail指向头尾Entry节点的指针,LinkedHashMap=HashMap+双向链表,通过双向链表维护节点的顺序,支持按插入(默认)或者访问顺序排序;LinkedHashMap中的节点元素为Entry<K,V>直接继承HashMap.Node<K,V>,增加了before/after指向前后Entry节点的指针;

ArrayList与Vector区别

Vector是ArrayList的线程安全版本,ArrayList和Vector绝大部分方法都是相同的,只是Vector的方法增加了synchronized修饰;ArrayList使用transient Object[] elementData,实现了writeObject、readObject方法定制序列化,Vector没有transient,只实现了writeObject方法,没有完全定制,从序列化机制的角度来看,ArrayList的实现比Vector的实现更安全; 扩容时,ArrayList扩充50%,Vector扩充一倍,会有更多的空间浪费;

ArrayList与LinkedList的实现和区别

ArrayList是基于动态数组实现,LinkedList基于双向链表实现;对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针;对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动其后的所有数据;

TreeMap:了解数据结构、了解其key对象为什么必须要实现Compare接口、如何用它实现一致性哈希

TreeMap基于红黑树(自平衡二叉排序树)实现,要求Key要能自然排序(实现了Comparable接口)或者构造时传入Comparator,由于是红黑树所以添加删除时需要维护红黑树的结构,containsKey/get/put/remove的时间复杂度是log(n),实现了NavigableMap接口,所以有一系列的导航方法;

 TreeMap基于红黑树实现,可用来实现一致性hash算法,原因是可以存储有序的数据、查询效率较高O(logn)、提供了一些高效的导航的方法可以直接使用如tailMap方法可以查找比n大的集合、增加虚拟节点比较方便; 实现代码

Java中的HashSet内部是如何工作的

HashSet内部采用HashMap来实现,构造函数时内部会new HashMap实现,由于Map需要key和value,所以HashSet中所有key的都有一个默认value,其他方法都是调用Map的方法实现;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 原文地址 Java集合 Java集合框架:是一种工具类,就像是一个容器可以存储任意数量的具有共同属性的对象。 Ja...
    gyl_coder阅读 958评论 0 8
  • ArrayList实现原理要点概括 参考文献:http://zhangshixi.iteye.com/blog/6...
    晨光光阅读 1,032评论 0 1
  • Java集合类可用于存储数量不等的对象,并可以实现常用的数据结构如栈,队列等,Java集合还可以用于保存具有映射关...
    小徐andorid阅读 1,849评论 0 13
  • 一、集合简介 1.1集合定义 是容器,存储任意数量的具有共同属性的对象 1.2集合作用 (1)在类的内部,对数据进...
    这一刻_776b阅读 208评论 0 0
  • title: java集合框架学习总结 tags:集合框架 categories:总结 date: 2017-03...
    行径行阅读 1,637评论 0 2