Java-集合面试题

2 篇文章 0 订阅

目录

一、为什么Java设计了迭代器Iterator?

二、ArrayList和LinkedList哪个更占空间?

三、Iterator和ListIterator区别?

四、Java的HashMap与Redis中的Hash比较?

五、Java的HashMap与与ConcurrentHashMap扩容的策略比较?


一、为什么Java设计了迭代器Iterator?

  1. 因为容器的内部结构不同,很多时候可能不知道该怎样去遍历一个容器中的元素。所以为了使对容器内元素的操作更为简单,Java引入了迭代器模式! 
  2. 把访问逻辑从不同类型的集合类中抽取出来,从而避免向外部暴露集合的内部结构。

 

二、ArrayList和LinkedList哪个更占空间?

一般情况下,LinkedList的占用空间更大,因为每个节点要维护指向前后地址的两个节点,但也不是绝对,如果刚好数据量超过ArrayList默认的临时值时,ArrayList占用的空间也是不小的,因为扩容的原因会浪费将近原来数组一半的容量,不过,因为ArrayList的数组变量是用transient关键字修饰的,如果集合本身需要做序列化操作的话,ArrayList这部分多余的空间不会被序列化。

 

三、Iterator和ListIterator区别?

ConcurrentModificationException异常:

https://www.cnblogs.com/codderYouzg/p/12416590.html

定义:并发修改异常 出现原因: 我们的迭代依赖于集合, 当我们往集合中添加好了元素之后 获取迭代器 那么迭代器已经知道了集合的元素个数 这个时候你在遍历的时候又突然想给 集合里面增、删、改元素(用的是集合的add方法),就会与已生成的迭代器对于数量的记录产生冲突,就会报异常 解决方法: 用ListIterator迭代器遍历 用迭代器自带的add、remove、set方法添加元素 那就不会报错了

List和Set都有iterator()来取得其迭代器。对List来说,你也可以通过listIterator()取得其迭代器,两种迭代器在有些时候是不能通用的

Iterator和ListIterator主要区别在以下方面:

  1. ListIterator有add()方法,可以向List中添加对象,而Iterator不能
  2. ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。
  3. ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
  4. 都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。

 

四、Java的HashMap与Redis中的Hash比较?

从数据结构的角度来看,redis的dict(字典)和java的HashMap很像,区别在于rehash:

  1. HashMap在resize时是一次性拷贝的,然后使用新的数组,
  2. dict维持了2个dictht,平常使用ht[0],一旦开始rehash则使用ht[0]和ht[1],rehash被分摊到每次的dictAdd和dictFind等操作中。

      当hash内部的元素比较拥挤时(hash碰撞比较频繁),就需要进行扩容。扩容需要申请新的两倍大小的数组,然后将所有的键值对重新分配到新的数组下标对应的链表中(rehash)。如果hash结构很大,比如有上百万个键值对,那么一次完整rehash的过程就会耗时很长。这对于单线程的Redis里来说有点压力山大。所以Redis采用了渐进式rehash的方案。它会同时保留两个新旧hash结构,在后续的定时任务以及hash结构的读写指令中将旧结构的元素逐渐迁移到新的结构中。这样就可以避免因扩容导致的线程卡顿现象。

 

五、Java的HashMap与与ConcurrentHashMap扩容的策略比较?

ConcurrentHashMap采用的扩容策略为: “多线程协同式rehash“。

这里的多线程指的是,有多个线程并发的把数据从旧的容器搬运到新的容器中。扩容时大致过程如下:

  1. 线程A在扩容把数据从oldTable搬到到newTable,这时其他线程
  2. 进行get操作:这个线程知道数据存放在oldTable或是newTable中,直接取即可。
  3. 进行写操作:如果要写的桶位,已经被线程A搬运到了newTable。那么这个线程知道正在扩容,它也一起帮着扩容,扩容完成后才进行put操作。
  4. 进行删除操作:与写一致。

两者对比:

  1. 扩容所花费的时间对比: 一个单线程渐进扩容,一个多线程协同扩容。在平均的情况下,是ConcurrentHashMap 快。这也意味着,扩容时所需要 花费的空间能够更快的进行释放。
  2. 读操作,两者性能相差不多。
  3. 写操作,Redis的字典返回更快些,因为它不像ConcurrentHashMap那样去帮着扩容(当要写的桶位已经搬到了newTable时),等扩容完才能进行操作。
  4. 删除操作,与写一样。

 

 

 

未完待续...

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值