集合(二)
- 1、为什么集合类没有实现Cloneable和Serializable接口
- 2、Java集合类框架的基本接口有哪些
- 3、ConcurrentHashMap的原理
- 4、解释一下TreeMap
- 5、ArrayList是否会越界?
- 6、ConcurrentHashMap有什么优势以及1.7和1.8的区别
- 7、TreeMap的底层实现
- 8、CooncurrentHashMap锁加在了那些地方
- 9、为什么HashMap的容量是2的n次幂
- 10、ArrayList和LinkedList的区别,并说明如果一直在list的尾部添加元素,用那种方式更加高效
- 11、如果Hash Map的key是一个自定义的类,怎么办
- 12、hashMap具体如何实现的?
- 13、HashMap和ConcurrentHashMap的区别
1、为什么集合类没有实现Cloneable和Serializable接口
克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。
实现serialization序列化的作用:将对象的状态保存在存储媒体中以便可以在以后重写创建出完全相同的副本;按值将对象从一个应用程序域发向另一个应用程序域。
实现Serialization接口的作用就是可以把对象存到字节流,然后可以恢复。要网络传输就得转为字节流,所以在分布式应用中,你就得实现序列化。如果不需要分布式应用,就没必要实现序列化。
2、Java集合类框架的基本接口有哪些
集合类接口指定了一组叫做元素的对象。集合类接口的每一种具体的实现类都可以选择它自己的方式对元素进行保存和排序。有的集合类允许重复的键,有些不允许。
Java集合类里面有的最基本的接口:
1.Collection:代表一组对象,每一个对象都是他的子对象。
2.Set:不包含重复元素的Collection
3.List:有顺序的collection,并且可以包含重复元素。
Map:可以把键映射到值对象,键不能重复。
3、ConcurrentHashMap的原理
ConcurrentHashMap类中包含两个静态内部类 HashEntry和Segment。
HashEntry用来封装映射表的键/值对;
Segment用来充当锁的角色,每个Segment对象守护整个散列映射表的若干个桶。每个桶是由若干个Hashentry对象连接起来的链表。一个ConcurrentHashMap实例中包含若干个Segment对象组成的数组。
HashEntry用来封装散列映射表中的键值对,在HashEntry类中,key,hash,next域都被声明为final型,value域被声明为volatile型
static final class HashEntry<K,V> {
final K key; //声明key为final型
final int hash; //声明hash为final型
volatile V value; //声明value为volatile型
final HashEntry<K,V> next; //声明next为final型
HashEntry(K key, int hash, HashEntry<K,V> next, V value){
this.key = key;
this.hash = hash;
this.next = next;
this.value = value;
}
}
在ConcurrentHashMap中,在散列时如果产生“碰撞”,将采用“分离链接法”来处理“碰撞”:把“碰撞”的HashEntry对象链接成一个链表。由于HashEntry的next域为final型,所以新节点只能在链表的表头处插入。
注意:由于只能在表头插入,所以链表中的节点顺序和插入顺序相反
Segment类继承域ReentrantLock类,从而使得Segment对象能充当锁角色。每个Segment对象用来守护其(成员对象table中)包含的若干个桶。
4、解释一下TreeMap
TreeMap是一个有序的key-value集合,基于红黑树的NavigableMap实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的Comparator进行排序,具体取决于使用的构造方法:
TreeMap的特性:
根节点是黑色;
每个节点都只能是红色或者黑色;
每个叶节点(NIL节点,空节点)是黑色的;
如果一个节点是红色的,则他两个子节点都是黑色的,也就是说一条路径上不能出现两个红色的节点;
从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
5、ArrayList是否会越界?
1.ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构;
2.对于随机访问get和set,ArrayList要优于LinkedList,因为LinedList要移动指针;ArrayList并发add()可能出现数组下标越界异常。
6、ConcurrentHashMap有什么优势以及1.7和1.8的区别
ConcurrentHashMap是线程安全的。jdk1.7中采用了Segment+HashEntry的方式进行实现的,Lock加载segment上面。1.7size计算式先采用不加锁的方式,连续计算元素的个数,最多计算三次:1.如果前后两次计算结果相同,则说明计算出来的元素个数是准确的;2.如果前后两次计算结果不同,则给每个Segment进行加锁,再计算一次元素的个数;1.8中放弃了Segment臃肿的设计,取而代之的是采用Node+CAS+Synchronized来保证并发安全进行实现,1.8中使用一个volatile类型的变量baseCount记录元素的个数,当插入新数据或删除数据时,都会通过addCount()方法更新baseCount,通过累加BaseCount和CounterCell数组中的数量,即可得到元素的总个数
7、TreeMap的底层实现
TreeMap的实现就是红黑树数据结构,也就是说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点。
红黑树的插入、删除、遍历时间复杂度都是O(IgN),所以性能上低于哈希表。但是哈希表无法提供键值对的有序输出,红黑树因为是排序插入的,可以按照键的值大小有效输出
8、CooncurrentHashMap锁加在了那些地方
加在了每个Segment上面
9、为什么HashMap的容量是2的n次幂
负载因子默认是0.75,2^n是为了让散列更加均匀,例如出现极端情况都在散列数组中的一个下标,那么hashmap会有O(1)复杂退化为O(n)的
10、ArrayList和LinkedList的区别,并说明如果一直在list的尾部添加元素,用那种方式更加高效
ArrayList采用数组实现的,查找效率比LinkedList高,LinkedList采用双向链表实现的,插入和删除的效率比Array List高。
一直在List的尾部添加元素,LinkedList的效率要高
11、如果Hash Map的key是一个自定义的类,怎么办
使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()。
12、hashMap具体如何实现的?
HashMap基于数组实现的,通过对key的hashcode&数组长度得到在数组中位置,如当前数组有元素,则数组当前元素next指向要插入的元素,这样来解决hash冲突的,形成了拉链式结构,put时在多线程情况下,会形成环从而导致死循环。数组长度一般是2n,从0开始编码,所以hashcode&(2n-1),(2n-1)每位都是1,这样会让散列均匀。需要注意的是,HashMap在Jdk1.8版本中引入了红黑树结构做优化,当链表元素个数大于8时,链表转换成树结构;若桶中链表元素小于等于6时,树结构还原成链表。因为红黑树的平均查找长度是log(n),长度为8时,平均查找长度为3,如果继续使用链表,平均查找长度为8/2=4,这才有转换为树的必要。链表长度如果小于等于6,6/2=3,虽然速度也很快,但是转化为树结构和生成树的时间 并不会太短。还有选择6和8,中间有个差值7可以有效的防止链表和树频繁转换。假设一下,如果设计成链表个数超过8则树结构转换成链表,链表个数小于8则树结构转换为链表,如果一个hashmap不停的插入删除,链表个数在8左右徘徊,就会频繁的发生树转链表,链表转树,效率会很低
13、HashMap和ConcurrentHashMap的区别
hashmap是线程不安全的,put时在多线程情况下,会形成环从而导致死循环。
Concurrenthashmap是线程安全的,采用分段锁机制,减少锁的粒度。