String和StringBuilder和String Buffer的区别?
String是不可变的,如果尝试修改,会新生成一个字符串对象,而StringBuilder和StringBuffer是可变的。
StringBuffer是线程安全的,String Builder是线程不安全的。所以在单线程String Builder效率更高,而多线程则采用String Buffer。
泛型中extends和super的区别?
< ? extends T>表示包括T在内的任何T的子类
<? super T>表示包括T在内的任何T的父类
==和equals的区别?
==:如果是基本数据类型,比较是值,如果是引用数据类型,比较的是引用地址。
Equals:具体看各个类型重写equals方法之后的比较逻辑比如String类,虽然是引用数据类型,但它重写了equals方法,比较的是各个字符是否相等。
重载和重写的区别?
重载:(OverLoad)在一个类中,同名的方法如果有不同的参数列表(参数类型不同,参数个数不同)则为重载。
重写:(OverRide)在子类中把父类的方法重写一遍,子类继承了父类的方法,但有时子类并不想原封不动的继承父类的方法,所以在方法名,参数列表,返回类型都相同的情况下,对方法体进行修改,这就是重写。
List和Set的区别?
List:有序,按对象插入的顺序保存对象,可重复,允许多个null元素,可以使用Iterator方法遍历所有元素,还可以使用get(int index)获取指定下标的元素。
Set:无序,不可重复,最多允许有一个null元素,取元素只能用Iterator逐一遍历所有元素。
Array List和LinkedList的区别?
首先,他们的底层结构不同,Array List是基于数组实现的,而Linked List是基于链表实现的。
由于底层数据结构不同,他们使用场景也不同,Array List适用于随机查找,Linked List适用于删除和添加,查询,删除和添加的时间复杂度不同。
另外Array List和Linked List都实现了List接口,但是LinkedList还实现了Deque接口,所以Linked List还可以当队列来使用。
谈谈concurrentHashMap的扩容机制?
JDK1.7:
1.7的Concurrent Hash Map是基于Segment实现的。
每个Segment都是一个小的Hash Map,每个Segment内部都会进行扩容,和Hash Map的扩容机制类似先生成数组,在转移元素到新数组中。
扩容的判断是每个Segment单独判断的,是否超过阈值。
JDK1.8:
1.8的版本不再是通过Segment实现的。
当某个线程进行put时,如果发现concurrentHashMap正在进行扩容,那么该线程一起进行扩容。
如果某个线程put时,发现concurrentHashMap没有进行扩容,则将key value添加到Hashmap中,然后判断是否超过阈值,超过则进行扩容。
ConcurrentHash Map支持多线程同时扩容。
扩容前也是新生成一个新数组,转移元素时,先将原数组分类,将每组分给不同的线程进行元素转移,每个线程负责一组或多组的元素转移工作。
JDK1.7到JDK1.8HashMap发生了什么变化?
1:1.7中底层使用数组+链表 ,而1.8中使用数组+链表+红黑树,目的是提高查询和插入效率。
1.7使用头插法,而1.8使用尾插法,因为1.8中插入key和value时要判断链表元素个数,所以正好用尾插法。
1.7哈希算法比较复杂,存在各种异或右移运算,1.8进行了简化,加进红黑树简化哈希算法,节省CPU资源。
说一下Hash Map的put方法?
Put的大体流程:
1:根据key通过哈希算法和与运算得出数组下标;
2:如果数组下标位置元素为空,则将key和value封装为Entry对象,(1.7是Entry,1.8是Node)放入该位置。
3:如果下表位置不为空,分情况讨论:
a:如果是JDK1.7,则会先判断是否需要扩容,如果要进行扩容就进行扩容,否则生成Entry对象用头插法插入当前链表中。
b:如果是JDK1.8,则会先判断当前位置上的Node的类型,看是红黑树Node还是链表Node。
- :如果是红黑树Node,则将Key和Value封装成一个红黑树节点并添加到红黑树中去,在这个过程中会判断红黑树是否存在当前key,如果存在则更新value.
- :如果此位置上的Node对象是链表节点,则将key和value封装成一个链表Node并通过尾插法插入到最后的位置去,因为是尾插法,所以要遍历整个链表,遍历过程中判断是否存在当前key,存在则更新value,遍历完后,将新节点插入到链表中去,查看节点个数,若大于8,则将链表转为红黑树。
- 将key和value封装插入到红黑树或者链表中,判断是否要扩容,不需要则结束put方法。
HashMap的扩容机制?
1.7版本
先生成新数组,
遍历老数组中每个位置上的元素
取每个元素的key,并基于新数组长度,计算每个元素在新数组下标。
将元素添加到新数组。
所有元素转移完了后,将新数组赋值给Hash Map的table属性。
1.8版本
先生成新数组。
遍历每个位置的数组或红黑树
如果是链表,则直接计算下表添加到新数组。
如果是红黑树,先遍历红黑树,计算每个元素在新数组的下标
a:统计每个下标元素个数
b:如果该位置元素超过8个,则生成新的红黑树,并将根节点添加到新数组的对应位置
c:如果元素没有超过8,则生成一个链表,并将链表头节点添加到数组的对应位置
所有元素转移完后,将新数组赋值给HahsMap的table属性。