目录
6.ArrayList和Vector的区别(带一下CopyOnWriteArrayList)
7.Array怎么转换为ArrayList,ArrayList怎么转换为Array
9.HashMap和Hashtable的区别(带一下ConcurrentHashMap)
11.HashMap的底层原理(数据结构+put()流程+resize()扩容)
13.什么是哈希碰撞/哈希冲突,怎么解决哈希冲突,HashMap采用的是什么策略?
14. HashMap为什么不直接使用key的hashCode()函数返回的哈希码作为落槽时的索引号?那HashMap是怎么解决呢?
16.为什么重写了equals()方法,必须也要重写hashcode( )方法
集合容器整理
1.Collection和Map的区别
1)Collection和Map是官方提供的集合容器的两大体系的顶层接口
2)Collection代表单元素集合体系
3)Map代表k v 键值对集合体系
4)Collection体系继承了iterable(迭代器)接口,所有的子类都提供了迭代器的实现,Map 体系没有
2.List,Set,Queue的区别
1)List,Set,Queue都是Collection体系下的子接口,分别代表三个体系
2)List体系的特点:有序不唯一
3)Set体系特点:无序,唯一
4)Queue体系特点:队列,先入先出
3.队列(Queue)和栈(Stack)的区别
1)队列是一种FIFO(First in First Out)先入先出的结构
2)栈是一种FILO(First in Last Out)先去后出的结构
Java集合体系中的LinkedList类可以实现队列和栈结构
在链表头部插入,尾部取出或者尾部插入头部取出,就是队列(插入和取出在不同方向上进行)
在链表的头部插入,头部取出或者尾部插入,尾部取出,就是栈(插入和取出在相同方向上进行)
4.Array和ArrayList的区别
Array是数组,ArrayList是类
Array是定长的(需要手动扩容),ArrayList长度可变(使用过程中自动扩容)
ArrayList的底层是Array
5.ArrayList和LinkedList的区别
1.底层数据结构实现:ArrayList底层数据结构是动态数组,而 Linkedlist的底层数据结构是双向链表
2.随机访问(即读)效率:ArrayList比LinkedList在随机访问的时候效率要高,因为ArrayList底层是数组,可以通过索引号快速访问,LinkedList是通过二分查找法遍历链表节点进行查找的
3.增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList增删操作需要大量的前移或后移,这个过程中涉及到大量的赋值操作比较耗时间, LinkedList只需要修改节点对象的左右指针即可。
4. 内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList
的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素
5.综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList
6.ArrayList和Vector的区别(带一下CopyOnWriteArrayList)
ArrayList是线程不安全的, Vector是线程安全的。ArrayList中所有的方法都没有加同步锁,Vector中所有的方法都加了sycholnizzed同步锁。官方在JDK1.5版本中又推出了CopyOnWriteArrayList,使用了lock锁实现线程安全,弃用了Vector,因为lock锁性能比sycholnizzed锁好。
在并发编程中,如果多个线程共享一个ArrayList,那么必须考虑线程安全的问题,可以自己在代码中对ArrayList操作代码加锁,或者直接用线程安全的CopyOnWriteArrayList类,在不考虑线程安全的环境下用ArrayList性能更好,因为加锁、开锁是很耗性能的。
7.Array怎么转换为ArrayList,ArrayList怎么转换为Array
1)官方提供的数组工具类Arrays中提供了一个静态方法Arrays.asList( )可以把数组转化为List,参数是数组,返回值是List
2)ArrayList类中提供了toArray()成员方法,可以把ArrayList转化为Array后进行返回
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Demo04 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李三");
list.add("王三");
//直接调用返回Object数组
//调用toArray的时候给一个空数组来告诉它需要的数组类型
String[] array = list.toArray(new String[]{});
a(array);
b(Arrays.asList(array));
}
static void a(String[] arr){
for (String s : arr) {
System.out.println(s);
}
}
static void b(List<String> list){
for (String s : list) {
System.out.println(s);
}
}
}
8.HashSet和TreeSet的区别
HashSet和TreeSet都是Set接口下面的子类
HashSet的底层是HashMap,它将数据存储在HashMap的key中
HashSet是无序的,唯一的,因为HashMap的key是无序唯一的
TreeSet的底层是TreeMap,它将数据存储在TreeMap的key中
TreeSet是有序的,唯一的,因为TreeMap的key是有序唯一的
9.HashMap和Hashtable的区别(带一下ConcurrentHashMap)
HashMap是线程不安全的, Hashtable是线程安全的。HashMap中所有的方法都没有加同步锁,Hashtable中所有的方法都加了sycholnizzed同步锁。官方在JDK1.5版本中又推出了ConcurrentHashMap,使用了lock锁实现线程安全,弃用了Hashtable,因为lock锁性能比sycholnizzed锁好。Hashtbale采用的是锁全表的机制,ConcurrentHashMap采用了分段锁的设计,锁粒度更细,性能更好。
在并发编程中,如果多个线程共享一个HashMap,那么必须考虑线程安全的问题,可以自己在代码中对HashMap操作代码加锁,或者直接用线程安全的ConcurrentHashMap类,在不考虑线程安全的环境下用HashMap性能更好,因为加锁、开锁是很耗性能的。
对Null key支持:HashMap支持key为null,但只能有一个,Hashtable不支持,会直接抛NPE,HashMap和Hashtable支持value为空,不限制个数。ConcurrentHashMap的key和value都不支持null。
HashMap在1.8以后,设置了阈值=8,当链表长度超过阈值的时候,会转换为红黑树以减少检索时间,Hashtable被弃用了,没有更新
初始容量大小和扩容容量大小的区别:
HashMap初始容量是16,扩容策略是原来的2倍
Hashtable默认初始容量是11,扩容策略是原来的2n+1
HashMap如果手动指定了初始容量,不是2的n次方它也会找到最近的一个2的n次方作为初始容量,Hashtable如果手动指定了初始容量,会直接使用给定的大小
10.HashMap和TreeMap的区别
HashMap底层是数组+链表/红黑树,key是无序的唯一的
TreeMap底层是红黑树,key是有序的唯一的
HashMap的性能比TreeMap更好,但如果需要一个有序的key的集合就需要使用TreeMap
11.HashMap的底层原理(数据结构+put()流程+resize()扩容)
12.HashMap的长度为什么是2的幂次方
为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。
13.什么是哈希碰撞/哈希冲突,怎么解决哈希冲突,HashMap采用的是什么策略?
如果有两个不同字符串通过同样的哈希算法计算出来的哈希码是一样的,则称他们发生了哈希碰撞,哈希冲突,解决方法:
1.开放地址法
2.拉链法(链地址法)HashMap默认使用的就是这种
3.再哈希法
4.建立公共溢出区
14. HashMap为什么不直接使用key的hashCode()函数返回的哈希码作为落槽时的索引号?那HashMap是怎么解决呢?
15.==和equals()方法的区别
==和equals()都可以用于比较,语法是a==b 或者a.equals(b)
==比较的是内存地址
equals()方法是Object类中方法,可以被任意类继承或重写,通过看官方Object类原码,equals方法默认也是用==比较内存地址
如果想要修改equals()方法的规则,可以重写equals()方法,String类就重写equals()方法的比较规则,由默认的比较两个字符串对象的内存地址,修改为比较字符串中每个字符是否相等。
因为堆区中可能会出现两个一模一样的字符串对象,但内存地址不一样,所以字符串的比较必须用equals()方法,否则可能会出现两个内容一模一样的字符串,因为地址不一样比较后出现不相等的情况。
16.为什么重写了equals()方法,必须也要重写hashcode( )方法
HashMap的底层采用了key的hashcode()来计算数组的索引 index,
如果数组 [index]为null,说明key不存在,直接落槽插入
如果数组 [index]不为null,说明该位置有key存在,但不能一定说明已存在的key与要插入的key重复,因为可能会发生哈希碰撞,此时应该进一步用equals方法比较已存在的key与要插入的key是否相等,如果相等就说明一定是重复的,应该覆盖,如果不相等,说明发生了哈希碰撞,那么应该插入在链表中。
重写equals方法的目的是为了不去比较两个对象的内存地址,改为比较对象的内容,如果一个类重写了equals没有重写hashcode,就可能出现两个地址不同的对象equals比较相等,但是hashcode比较不相等,这样会违反hashcode的唯一性,因此重写了equals方法必须也要重写hashcode方法,且必须满足两个对象equals相等,hashcode也必须相等