1.List、Set和Map对数据处理有什么区别?
数据 | List | Set | Map |
---|---|---|---|
数据结构 | 有序,允许重复 | 无序,不允许重复 | 键值对,键不允许重复 |
存储方法 | 数组或链表 | 哈希表 | 哈希表 |
操作方法 | 添加、删除、修改、获取、索引访问 | 添加、删除、判断存在 | 添加、删除、获取、根据键查找值 |
应用 | 元素排序,索引访问 | 元素唯一性,快速查找 | 存储键值对,快速查找值 |
2.Iterator 和 ListIterator 有什么区别?
特性 | Iterator | ListIterator |
---|---|---|
遍历方向 | 单向 | 双向 |
修改 | 只能读取或删除当前迭代对象 | 可读写 |
获取索引 | 无法获取 | 可以获取 |
添加元素 | 无法添加 | 可以添加 |
替换元素 | 无法替换 | 可以替换 |
问题:创建一个set集合,用一个迭代器迭代两次能得到结果吗?
答:不能,Set集合的特性是元素不能重复,如果迭代器已经遍历过所有元素,再次迭代将不会有任何新的元素可以获取,迭代器在创建后,只能遍历一次,当迭代器遍历完所有元素后,它就处于“已用完”的状态,无法再次使用。
3.遍历一个 List集合有哪些不同的方式?
(1)普通for循环
(2)增强for循环
(3)迭代器Iterator
(4)ListIterator
(5)Lamda表达式
(6)Stream流
4.ArrayList 和 Vector 的区别是什么?Stack呢?
特性 | ArrayList | Vector | Stack |
---|---|---|---|
线程安全 | 不是线程安全 | 线程安全 | 线程安全 |
性能 | 快 | 慢 | 慢 |
数据结构 | 动态数组 | 动态数组 | 栈 |
5.HashMap是怎么解决哈希冲突的?
开放定址法也称线性探测法,就是从发生冲突的那个位置开始,按照一定次序从Hash表找到一个空闲位置然后把发生冲突的元素存入到这个位置,而在java中,ThreadLocal就用到了线性探测法来解决Hash冲突。
特性 | HashMap | ConcurrentHashMap | Hashtable |
---|---|---|---|
线程安全性 | 不是线程安全 | 线程安全(分段锁机制) | 线程安全 |
性能 | 较快 | 优于 Hashtable | 较慢 |
使用场景 | 单线程环境 | 多线程环境,高并发性能 | 简单线程安全 |
ConcurrentHashMap的分段锁机制:
ConcurrentHashMap 将数据分成多个段 ,每个段都拥有自己的锁,类似于一个独立的 HashMap,当多个线程同时访问ConcurrentHashMap 时,它们可以同时访问不同的段,而不会互相阻塞,分段锁机制将锁的粒度从整个 HashMap 降低到每个段,从而减少了锁竞争,提高了并发性能。
6.为什么 ArrayList 的 elementData 加上 transient 修饰?
transient 关键字用于标识一个字段不应该被序列化。当一个对象被序列化时,transient 字段会被忽略,不会被写入到序列化流中。
拓展:序列化和反序列化
(1)序列化和反序列化的定义:
Java序列化就是指把Java对象转换为字节序列的过程
Java反序列化就是指把字节序列恢复为Java对象的过程。
(2)序列化最重要的作用:在传递和保存对象时.保证对象的完整性和可传递性。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。
反序列化的最重要的作用:根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。
(3)序列化过程:
将对象转换为字节流的过程。
使用 ObjectOutputStream 类将对象序列化为字节流。
序列化后的字节流可以存储在磁盘上或通过网络传输。
反序列化过程:
将字节流转换为对象的过程。
使用 ObjectInputStream 类将字节流反序列化为对象。
反序列化后的对象可以被程序使用。
数据格式:
Java 序列化使用一种自定义的二进制格式来存储对象数据,避免空间浪费,序列化后的字节流包含对象的类信息、字段名和字段值。
7.多线程场景下如何使用 ArrayList?
方式一:使用Collections.synchronizedList()
ArrayList 不是线程安全的,如果遇到多线程场景,
可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。
方式二:使用CopyOnWriteArrayList
copyOnWriteArrayList相当于每次使用都会复制一份新的ArrayList
保证隔离,这样子线程使用就安全了,但是有个缺点,就是比较费内存,而且如果是增删改场景较多的时候,数组复制会浪费较多的时间
拓展:CopyOnWriteArrayList
CopyOnWriteArrayList 是 Java 中的一个线程安全的 ArrayList 实现,它在进行添加、删除或修改操作时,会创建一个新的数组来存储数据,并复制原数组中的所有元素。这种机制保证了在进行写操作时,不会影响其他线程的读操作。
工作原理:当进行添加、删除或修改操作时,CopyOnWriteArrayList 会创建一个新的数组,并将原数组中的所有元素复制到新数组中,CopyOnWriteArrayList 会在新的数组中进行相应的操作,将内部引用指向新的数组,从而完成写操作。
特点:
线程安全: CopyOnWriteArrayList 是线程安全的,因为在进行写操作时,它会创建一个新的数组,不会影响其他线程的读操作。
读操作快: 由于读操作不会进行任何同步操作,因此读操作的性能非常高。
写操作慢: 写操作需要创建新的数组并复制元素,因此写操作的性能较低。
内存占用高: CopyOnWriteArrayList 会占用额外的内存来存储新的数组,因此内存占用相对较高。
8.红黑树
红黑树是一种自平衡的二叉查找树,红黑树牺牲了严格的平衡性,换取了更快的插入/删除操作速度和更少的旋转操作,它是一种近似平衡的二叉树,它能够确保任何一个结点的左右子树的高度差不会超过两倍。红黑树需要满足以下五个性质:
每个结点要么是红色,要么是黑色。
根结点必须是黑色。
红色结点的子节点必须是黑色。
对于每个结点,从该结点到其所有后代叶子结点的简单路径上,均包含相同数目的黑色结点。
空结点被认为是黑色的。
9.快速失败机制 “fail-fast”
快速失败机制(Fail-Fast)是 Java 中的一种错误检测机制,它通过在迭代器遍历集合的过程中检测集合是否被修改来实现,如果在迭代过程中发现集合被修改了,迭代器会抛出 ConcurrentModificationException 异常,阻止程序继续执行。
特点:
快速检测: 快速失败机制能够快速检测到集合被修改的情况,避免潜在的错误。
异常抛出: 当检测到集合被修改时,快速失败机制会抛出 ConcurrentModificationException 异常,提醒程序员处理错误。
非线程安全: 快速失败机制本身并不保证线程安全,它只是在迭代过程中检测到集合被修改时抛出异常。
10.HashMap 的长度为什么是 2 的幂次方?
为了能让HashMap存取高效,尽量减少碰撞,需要将散列表的数据分配均匀。
补充:使用HashMap查询或插入数据时,需要先对数组长度取模运算,index = hash%length,得到余数用来存放位置,也就是对应的数组角标。取余操作中,如果除数是2的幂次,则等价于除数减一的与操作,即hash%length=hash&(length-1),这里采用&运算比%运算效率高,故HashMap的长度需是2的幂次方。
11.HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标?
(1)hashCode() 方法返回的是一个 int 类型的值,其范围是 2-31 到 231-1,如果直接使用 hashCode() 作为 table 的下标,则需要一个容量为 231 的 table,这会占用巨大的内存空间不利于提升性能。
(2)如果直接使用 hashCode() 作为 table 的下标,会出现具有相同 hashCode() 值的 key 会被映射到同一个位置,出现哈希冲突,导致数据存储和查找效率低。