List、Set、Map等常用集合类的特点和用法。
常用集合类(List、Set、Map 等)是 Java 中提供的数据结构,用于存储和操作一组数据。以下是它们的特点和用法:
- List(列表):
- 特点:有序集合,允许重复元素。
- 用法:常用的实现类有 ArrayList 和 LinkedList。可通过索引访问元素,支持添加、删除和修改操作。适用于需要维护元素顺序且可能包含重复元素的场景。
ArrayList:
- 特点:基于数组实现的动态数组,支持快速随机访问。
- 用法:适用于频繁访问和更新元素的场景,但在插入和删除元素时效率较低。
LinkedList:
- 特点:基于链表实现的双向链表,支持快速插入和删除操作。
- 用法:适用于频繁插入和删除元素的场景,但在随机访问元素时效率较低。
- Set(集合):
- 特点:无序集合,不允许重复元素。
- 用法:常用的实现类有 HashSet 和 TreeSet。不保持元素插入顺序,不允许重复元素的存在。适用于需要去重的场景。
HashSet:
- 特点:基于哈希表实现的无序集合,不允许重复元素。
- 用法:适用于需要快速查找和去重的场景,但不保持元素的插入顺序。
TreeSet:
- 特点:基于红黑树实现的有序集合,不允许重复元素。
- 用法:适用于需要按照自然排序或自定义排序方式对元素进行排序的场景。
- Map(映射):
- 特点:键值对的集合,键和值可以都是任意对象,键不允许重复。
- 用法:常用的实现类有 HashMap 和 TreeMap。通过键来获取值,键是唯一的,适用于需要根据键查找值的场景。
HashMap:
- 特点:基于哈希表实现的键值对集合,键和值可以为 null。
- 用法:适用于快速查找和存储键值对的场景,根据键来获取值,不保持插入顺序。
TreeMap:
- 特点:基于红黑树实现的有序映射,键不允许为 null。
- 用法:适用于需要按照自然排序或自定义排序方式对键值对进行排序的场景。
1.List、Set 和 Map 之间有什么区别?它们的常用实现类有哪些?
List、Set和Map是Java集合框架中的三个核心接口,它们之间的区别如下:
List:
list是有序可重复的集合接口,可以按照顺序或者指定的顺序访问和操作元素,还可以通过索引访问元素
常用实现类:ArrayList、LinkedList
Set:
set是无序不重复的集合接口,不能通过索引访问元素。
常用实现类:HashSet、TreeSet
Map:
map是键值对映射的集合接口,每个键只能对应一个值。键值均可以为空
常用实现类:HashMap、ConcurrentHashMap
2.ArrayList 和 LinkedList 的区别是什么?它们适用于不同的场景吗?
ArrayList:底层是数组,查询快增删慢,可以通过索引随机访问,时间复杂度是O(1),
LinkedList: 底层是链表,查询慢增删块,不能使用索引访问,需要从头结点或者尾结点开始遍历,时间复杂度为O(n)
ArrayList适用于需要快速随机访问元素的场景,例如需要频繁地根据索引读取或修改元素的情况。
LinkedList适用于需要频繁进行插入和删除操作的场景,特别是在中间或开头进行插入和删除操作比较多的情况。
对于大部分常见的情况,ArrayList的性能要优于LinkedList,因为数组的访问速度更快。
但在某些特定的场景下,LinkedList可能会更适合,例如需要频繁进行插入和删除操作,并且对于随机访问的性能要求较低的情况。
3.HashSet 和 TreeSet 的区别是什么?它们如何保证元素的唯一性?
HashSet的底层是hash表且是无序不重复的集合接口
TreeSet的底层是红黑树,是有序不重复的集合接口
为了保证元素的唯一性,HashSet和TreeSet在判断元素是否重复时,依赖于元素的equals方法(和哈希码)
在选择HashSet和TreeSet时,需要根据具体的需求进行选择:
如果只关心元素的唯一性,而不关心元素的顺序,可以选择HashSet,它的插入、删除和查找操作的性能较好。
如果需要对元素进行排序,可以选择TreeSet,它会根据元素的顺序进行存储,但由于需要维护红黑树的平衡性,插入、删除和查找操作的性能稍低于HashSet。
总结:HashSet和TreeSet都可以保证元素的唯一性,HashSet适用于无序需求,而TreeSet适用于有序需求。
4.HashMap 和 HashTable 的区别是什么?它们的线程安全性如何?
HashMap是非线程安全的,存储的键和值都可以为null
而HashTable是线程安全的,存储的键和值都不可以为null
HashMap的性能通常比HashTable更好。
5.ConcurrentHashMap 是如何实现线程安全的?它与 HashMap 的区别是什么?
实现线程安全的几个关键点:
ConcurrentHashMap内部使用了分段式的锁,将整个数据结构分成一些独立的部分,并称为“段”并对不同的段进行了锁的力度的控制。还使用了volatile关键字来确保可见性,确保当一个线程修改了某个段的内容后,其他线程可以立即看到修改的结果。还使用并发安全的数据结构(hashEntry数组和链表)与线程安全的迭代器
与HashMap相比,ConcurrentHashMap的区别如下
:
ConcurrentHashMap是线程安全的,可以在多线程环境下进行并发操作,而HashMap是非线程安全的。在并发环境下,ConcurrentHashMap的性能通常比HashMap好,因为它通过分段锁的机制允许多个线程同时进行读操作,提高了并发性能。
线程安全的集合
1.常见的集合中的线程安全集合
List
1.Vector
原理是为其所有需要线程安全的方法都添加了synchronize关键字,锁住了整个对象(使用的互斥锁)
2.CopyOnWriteArrayList
在多线程中,读操作跟普通的ArrayList没有区别,写操作会上锁,上锁后将数据复制一份,再将数据写入,避免数据覆盖而造成的数据问题(使用读写锁)
Set
1.CopyOnWriteArraySet
底层使用的是CopyOnWriteArrayList
Queue
1.ConcurrentListedQueue
线程安全,读写效率高的队列,高并发情况下性能最好
其使用CAS比较交换算法来实现线程安全,其添加对象时涉及三个核心参数(V,E,N)
V:当前需要更新的变量,E:预期值,N:新值
只有当V=E时,才会将V=N,否则表示已经被别的线程更新,取消当前操作
2.BlockingQueue
https://blog.csdn.net/sjemYele/article/details/121004818ArrayBlockingQueue
LinkedBlockingQueue
2.Map中线程安全的
concurrentHashMap
是一个支持高并发更新与查询的哈希表(基于HashMap)。
在保证安全的前提下,进行检索不需要锁定。
HashTable
原理是为每个方法添加了synchronized关键字,来实现的线程安全,锁住了整个对象(使用的锁是互斥锁)
集合的遍历、排序、查找等操作。
遍历
1.如何遍历集合框架中的元素?有哪些遍历方式?
1.使用迭代器(Iterator):通过调用集合的iterator()方法获取迭代器对象,然后使用while循环和next()方法逐个访问元素,直到遍历完所有元素。
List<String> list = new ArrayList<>();
// 添加元素到列表中
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
// 处理元素
}
2.使用增强型for循环(foreach):适用于数组和实现了Iterable接口的集合类,可以直接通过for循环遍历元素,不需要显式使用迭代器。
List<String> list = new ArrayList<>();
// 添加元素到列表中
for (String element : list) {
// 处理元素
}
3.**使用Lambda表达式和Stream API:**从Java 8开始,引入了Lambda表达式和Stream API,可以通过Stream的forEach()方法对集合进行遍历,并结合Lambda表达式进行元素处理。
List<String> list = new ArrayList<>();
// 添加元素到列表中
list.stream().forEach(element -> {
// 处理元素
});
4.使用普通的for循环:适用于数组和实现了RandomAccess接口的集合类,通过下标访问元素进行遍历。
List<String> list = new ArrayList<>();
// 添加元素到列表中
for (int i = 0; i < list.size(); i++) {
String element = list.get(i);
// 处理元素
}
排序、查找
集合类提供了丰富的排序、查找和其他操作方法,以下是一些常见的操作:
排序操作:
- List:使用
Collections.sort(list)
方法对列表进行自然排序,或者使用实现了Comparable
接口的自定义对象进行排序。也可以使用Collections.sort(list, comparator)
方法根据自定义比较器进行排序。 - TreeSet:元素会自动按照自然顺序或者自定义比较器的顺序进行排序。
查找操作:
- List:可以使用
list.indexOf(element)
方法查找元素在列表中的索引,或者使用list.contains(element)
方法判断元素是否存在。 - Set:可以使用
set.contains(element)
方法判断元素是否存在。
其他操作:
- 添加元素:使用
add(element)
方法将元素添加到集合中。 - 删除元素:使用
remove(element)
方法从集合中删除指定元素。 - 遍历元素:可以使用迭代器或增强型循环来遍历集合中的元素。
- 获取集合大小:使用
size()
方法获取集合中元素的个数。
对于需要进行自定义排序或比较的场景,可以实现 Comparable
接口或创建自定义比较器(实现 Comparator
接口)来指定排序规则。
示例:
List<Integer> list = new ArrayList<>();
list.add(5);
list.add(3);
list.add(8);
Collections.sort(list); // 对列表进行自然排序
System.out.println(list); // 输出:[3, 5, 8]
List<String> strings = new ArrayList<>();
strings.add("banana");
strings.add("apple");
strings.add("cherry");
Collections.sort(strings, Collections.reverseOrder()); // 对列表进行逆序排序
System.out.println(strings); // 输出:[cherry, banana, apple]
Set<String> set = new TreeSet<>();
set.add("banana");
set.add("apple");
set.add("cherry");
System.out.println(set); // 输出:[apple, banana, cherry]
System.out.println(set.contains("apple")); // 输出:true
System.out.println(set.contains("orange")); // 输出:false
这些操作可以根据具体需求选择适合的集合类和方法,灵活地进行数据操作和处理。