集合/容器
- java 容器都有哪些?
- java容器那些是线程安全的,那些是线程不安全的?
- Collection 和 Collections 有什么区别?
- List、Set、Map 之间的区别是什么?
- HashMap 和 Hashtable 有什么区别?
- 如何决定使用 HashMap 还是 TreeMap?
- 说一下 HashMap 的实现原理?
- HashMap底层的数据结构是什么
- HashMap解决hash冲突的方法?
- 为什么在解决hash冲突的时候,不直接使用红黑树?而选择先用链表,再转红黑树呢?
- HashMap的扩容方式?
- HashMap为什么线程不安全?
- ConcurrentHashMap的实现原理
- ConcurrentHashMap不支持key或者value为null的原因?
- ConcurrentHashMap的并发度是多少?
- ConcurrentHashMap和HashTable的效率那个高?
- HashTable的锁机制?
- 说一下 HashSet 的实现原理?
- ArrayList 和 LinkedList 的区别是什么?
- 如何实现数组和 List 之间的转换?
- ArrayList的默认长度是多少
- ArrayList 的扩容机制
- Iterator 怎么使用?有什么特点?
- ArrayList特点
- LinkedList特点
- Stack特点
- Queue的特点
- 在 Queue 中 poll()和 remove()有什么区别?
java 容器都有哪些?
java容器那些是线程安全的,那些是线程不安全的?
线程安全的:
- HashTable
- concurrenthash
- vector
- stack
线程不安全的:
- hashmap
- ArrayList
- LinkedList
- HashSet
Collection 和 Collections 有什么区别?
- java.util.Collection是一个
接口
(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。 - Collections则是一个
类
(工具类、帮助类),其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
List、Set、Map 之间的区别是什么?
HashMap 和 Hashtable 有什么区别?
- HashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
- HashMap允许插入空键值,而hashTable不允许。
- HashMap是线程不安全的。HashTable是线程安全的(但HashTable的效率低)
如何决定使用 HashMap 还是 TreeMap?
对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。(TreeMap底层红黑树)
说一下 HashMap 的实现原理?
- 1.7是用数组+链表的方式实现的,采用的是头插法,会造成链表逆序的问题,在多线程下会产生循环链表,从而造成死循环(是线程不安全的)
- 1.8则采用数组+链表+红黑树的方式实现的,采用的是尾插法,但在多线程下会产生是数据覆盖的问题(是线程不安全的)
当我们往Hashmap中put元素时,首先根据key的hashcode重新计算hash值,根据hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,如果数组中该位置没有元素,就直接将该元素放到数组的该位置上。
HashMap底层的数据结构是什么
在JDK1.7中,由“数组+链表”组成,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的
在JDK1.8中,有“数组+链表+红黑树组成”,当链表过长时则会严重影响hashmap的性能。当链表长度超过8且数组长度超过64才会转成红黑树,如果链表长度大于8,但数组长度小于64的话会先进行扩容
HashMap解决hash冲突的方法?
hashmap采用的是链地址法,当key相同时,将key相同的元素构建成一个单链表,并将单链表头指针存放在数组的第i个单元中
为什么在解决hash冲突的时候,不直接使用红黑树?而选择先用链表,再转红黑树呢?
因为红黑树需要左旋、右旋、来保持平衡,而单链表则不需要,当元素个数小于8个时使用单链表查询效率高
HashMap的扩容方式?
hashmap在负载因子超过0.75时就会扩容,方法是将hashmap的数组扩大为原来的两倍,再将原来的对象放到新的数组里
HashMap为什么线程不安全?
- 多线程情况下扩容不安全。1.7采用的是头插法,在多线程情况下扩容的时候会导致环形链表的出现,会造成死循环。
- 1.8采用的是尾插法,但在多线程情况下进行put操作会出现数据覆盖的问题
ConcurrentHashMap的实现原理
ConcurrentHashMap在 JDK 1,7 和 JDK1.8的实现方式是不同的
- jdk1.7采用的是segment和hashentry实现的,segment是可重入锁,扮演锁的角色,hashentry用于存储键值对,首先将数据分为一段一段存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个数据的时候,其他段的数据也能被其他线程访问到,实现真正的并发访问
- 在jdk1.8中,concurrenthashmap采用了数组+链表+红黑树结构,在锁的实现上,抛弃了原有的Segment分段锁,采用CAS+Synchronized实现的,直接锁住当前链表的头结点(或者红黑树的根节点),就不会影响其他链表或者红黑树的读写,大大提高了并发
ConcurrentHashMap不支持key或者value为null的原因?
hashmap支持空的key和value,而ConcurrentHashMap为什么不支持呢?
先来说说value为什么不能为null,因为ConcurrentHashMap是用于多线程的,如果map.get(key)得到null,无法判断是映射的value为null还是没有找到key返回null,产生了二义性。
ConcurrentHashMap的并发度是多少?
- 在JDK1.7中,并发度默认是16,这个值可以在构造函数中设置。如果自己设置了并发度,ConcurrentHashMap会使用大于等于该值的最小的2的幂指数作为实际并发度,也就是比如你设置的值是17,那么实际并发度是32。
ConcurrentHashMap和HashTable的效率那个高?
ConcurrentHashMap的效率更高,因为ConcurrentHashMap使用的锁的粒度更低
HashTable的锁机制?
HashTable是基于synchronized实现的,是将整个哈希表锁起来,效率低下
说一下 HashSet 的实现原理?
- HashSet底层由HashMap实现
- HashSet的值存放于HashMap的key上
- HashMap的value统一为PRESENT
ArrayList 和 LinkedList 的区别是什么?
- ArrrayList底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构是双向循环链表,不支持随机访问
- ArrrayList的插入时间复杂度为O(n),而linkedList的复杂度为O(1)
- ArrrayList的查询时间复杂度为O(1),而linkedList的复杂度为O(n)
如何实现数组和 List 之间的转换?
- List转换成为数组:调用ArrayList的toArray方法。
- 数组转换成为List:调用Arrays的asList方法。
ArrayList的默认长度是多少
AarrayList默认长度为0,在调用add方法后,如果之前没有指定长度的话默认长度为10
ArrayList 的扩容机制
ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新的数组中去,默认情况下,新的容量会是原有容量的1.5倍
Iterator 怎么使用?有什么特点?
迭代器—不必知道序列底层是怎么实现的,就可以利用迭代器来访问一个序列。
Java中的Iterator功能比较简单,并且只能单向移动:
- (1)使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
- (2) 使用next()获得序列中的下一个元素
- (3) 使用hasNext()检查序列中是否还有元素。
- (4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
ArrayList特点
ArrayList底层是数组,默认采用尾插法,再remove()元素时,由于modCount所以需要使用迭代器来删除,它允许插入null。
LinkedList特点
LinkedList的底层结构是一个带头尾指针的双向链表,可以快速的对头、尾节点进行操作,它允许插入null。
Stack特点
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
栈底层用Vector实现的
Queue的特点
队列:只允许在一端进行插入数据操作,在另一端进行删除操作的特殊线性表,队列具有先进先出FIFO(First In First Out)的特点
在 Queue 中 poll()和 remove()有什么区别?
poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。