java基础 集合常见的面试题

103 阅读8分钟

集合常见的面试题:

Q&A HashMap的组成?

jdk 1.7时是数组+链表组成,

jdk1.8时是 数组+ 链表 + 红黑树组成。 链表元素大于等于8时会把链表转为树结构,若桶中链的元素个数小于等于6时,树结构还原成链表。当链表的个数为8左右徘徊时就会生成树转链表,链表转树,效率低下。hasMap的负载因子默认为0.75,2^n是为了散列更加均匀。

Q&A HashMap的key为自定义的类应该怎么办?

如果key为自定义的类应该重写hashcode()和equals()方法。

Q&A HashMap为何线程不安全?

1.在JDK1.7中,当并发执行扩容操作时会造成环形链和数据丢失的情况。 2.在JDK1.8中,在并发执行put操作时会发生数据覆盖的情况。

Q&A HashMap如何保证线程安全?

使用Collections.synchronizedMap()包装一下就可以了,原理就是对所有的修改操作都加上synchronized,保证了线程的安全。

Map  map = Collections.synchronizedMap(new HashMap());
Q&A HashMap中的key可以为任意类型吗?

不能使用基本类型,HashMap中key是可以为null, 只能存储一个null, 因为计算key的hash值的时候,如果key为null, 则其hash值为0

之所以key不能为基本数据类型,则是因为基本数据类型不能调用其hashcode()方法和equals()方法,进行比较,所以HashMap集合的key只能为引用数据类型,不能为基本数据类型,可以使用基本数据类型的包装类,例如Integer Double等。

Q&A HashMap 和 HashTable 区别?

HashMap和HashTable都实现了Map接口,HashMap允许键和值是null而HashTable不允许键和值是null,HashTable是同步的,而HashMap不是,因此hashMap适用于单线程环境,而HashTable适用于多线程环境。HashMap提供了可供应用迭代的键的集合。

Q&A HashMap和ConCurrentHashMap区别?

hashMap线程不安全,put时在多线程的情况下会形成环而导致循环。

ConCurrentHashMap是线程安全的,采用分段机制,减少锁粒度。

ConCurrentHashMap是线程安全,在jdk1.7时采用Segment+HashEntry的方式进行实现 lock加上Segment上面。1.7 size计算是线采用不加锁的方式。连续计算元素的个数,最多计算3次。

1.8中取而代之是采用Node+CAS +Synchronized来保证并发安全,1.8实现使用一个volatile类型的变量baseCount记录元素的各少数。当插入新数据或删除新数据时,会通过addCount()方法更新baseCount,通过累计baseCount和CounterCell数组中的数量,即可得到元素的总个数。

Q&A ConcurrentHashMap 和 HashTable 区别?

ConcurrentHashMap

  • 底层采用分段的数组+链表实现,线程安全
  • 通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
  • Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
  • 有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
  • 扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容

HashTable

  • 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
  • 初始size为11,扩容:newsize = olesize*2+1
  • 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

两则的区别:

hashtable线程安全,采用的是线程同步得方法。

  • ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。

  • Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

Q&A Linkedhashmap 与 hashmap 的区别?
  • HashMap
  • HashMap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。遍历时,取得数据的顺序是完全随机的。
  • HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null。
  • HashMap不支持线程的同步(即任一时刻可以有多个线程同时写HashMap),可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。
  • Hashtable与 HashMap类似,它继承自Dictionary类。不同的是:Hashtable不允许记录的键或者值为空;它支持线程的同步(即任一时刻只有一个线程能写Hashtable),因此也导致了 Hashtable在写入时会比较慢。
  • LinkedHashMap
  • 保存插入顺序:LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的。也可以在构造时带参数,按照应用次数排序。
  • 速度慢:在遍历的时候会比HashMap慢,不过有种情况例外:当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢。因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
Q&A hashmap 与 hashset 区别?

HashSet和HashMap之间有很多相似之处。对于HashSet而言,系统采用Hash算法决定集合元素的存储位置,这样可以保证快速存、取集合元素;对于HashMap而言,系统将value当成key的附属,系统根据Hash算法来决定key的存储位置,这样可以保证快速存、取集合key,而value总是紧随key存储。

HashSet的add()方法添加集合元素时实际上转变为调用HashMap的put()方法来添加 key-value对,当新放入 HashMap的Entry 中key 与集合中原有Entry的key 相同(hashCode()返回值相等,通过equals比较也返回true)时,新添加的Entry的value将覆盖原来Entry的value,但key不会有任何改变。因此,如果向HashSet中添加一个已经存在的元素,新添加的集合元素(底层由HashMap的key保存)不会覆盖已有的集合元素

Q&A ArrayList和Vector有何异同点?

相同点: (1)两者都是基于索引的,都是基于数组的。 (2)两者都维护插入顺序,我们可以根据插入顺序来获取元素。 (3)ArrayList 和 Vector 的迭代器实现都是 fail-fast 的。 (4)ArrayList 和 Vector 两者允许 null 值,也可以使用索引值对元素进行随机访问。 不同点: (1)Vector 是同步,线程安全,而 ArrayList 非同步,线程不安全。对于 ArrayList,如果 迭代时改变列表,应该使用 CopyOnWriteArrayList。 (2)但是,ArrayList 比 Vector 要快,它因为有同步,不会过载。 (3)在使用上,ArrayList 更加通用,因为 Collections 工具类容易获取同步列表和只读列 表。ArrayList在并发add()可能出现下标越界异常。

Q&A ArrayList 与 LinkedList 区别 ?

ArrayList 和 LinkedList 都实现了 List 接口,他们有以下的不同点: ArrayList 是基于索引的数据接口,它的底层是数组。它可以以 O(1)时间复杂度对元素进行 随机访问。与此对应,LinkedList 是以元素列表的形式存储它的数据,每一个元素都和它的 前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是 O(n)。 相对于 ArrayList,LinkedList 的插入,添加,删除操作速度更快,因为当元素被添加到集合 任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。 LinkedList 比 ArrayList 更占内存,因为 LinkedList 为每一个节点存储了两个引用,一个指 向前一个元素,一个指向下一个元素。

Q&A 数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用 Array 而不是 ArrayList?

array包含基本类型和对象类型。Arraylist只能包含对象类型。

Arraylist是采用数组实现的,arraylist是可以自动扩容的。比array提供了更多的特性,比如 addAll(),removeAll() 等。

Q&A 使用ArrayList的迭代器会出现什么问题?单线程和多线程环境下;

常用的迭代器设计模式,iterator 方法返回一个父类实现的迭代器。 1、迭代器的 hasNext 方法的作用是判断当前位置是否是数组最后一个位置,相等为 false, 否则为 true。 2、迭代器 next 方法用于返回当前的元素,并把指针指向下一个元素,值得注意的是,每次 使用 next 方法的时候,都会判断创建迭代器获取的这个容器的计数器 modCount 是否与此 时 的 不 相 等 , 不 相 等 说 明 集 合 的 大 小 被 修 改 过 , 如 果 是 会 抛 出 ConcurrentModificationException 异常,如果相等调用 get 方法返回元素即可