程序汪发现Java集合是写业务代码使用最高频的工具了,当然面试的时候也喜欢被问,把集合掌握好是非常必要的
-
集合中什么是fail-fast和fail-safe?
-
ArrayList 和 LinkedList 的区别是什么?
-
如何决定使用 HashMap 还是 TreeMap?
-
Java中怎么让集合的值不能修改?
集合中什么是fail-fast和fail-safe?
在Java中,fail-fast和fail-safe是两种不同的迭代器行为,特别是在遍历集合时遇到并发修改的情况。
Fail-Fast(快速失败)
快速失败机制意味着当迭代器创建后,如果集合被结构性修改(比如添加或删除元素),迭代器会立即抛出ConcurrentModificationException异常。这是通过在迭代器和集合之间维护一个称为“修改计数器”的机制实现的。每次集合修改,计数器加一,迭代器在每次迭代前都会检查这个计数器是否发生变化,从而决定是否抛出异常。
代码示例(快速失败):
importjava.util.ArrayList; importjava.util.ConcurrentModificationException; importjava.util.Iterator; importjava.util.List; publicclassFailFastExample{ publicstaticvoidmain(String[] args){ List<String> list =newArrayList<>(); list.add("A"); list.add("B"); list.add("C"); Iterator<String> iterator = list.iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); // 在迭代过程中尝试修改集合 list.remove("B");// 这将导致ConcurrentModificationException } } }
这段代码在遍历过程中尝试删除元素,将触发ConcurrentModificationException。
Fail-Safe(安全失败)
安全失败机制允许在遍历过程中对集合进行修改,而不会抛出异常。这通常是通过创建集合的一个快照或者使用一个独立的内部迭代器实现的,这样即使原集合被修改,迭代过程也不会受到影响。Java的CopyOnWriteArrayList和ConcurrentHashMap.KeySetView等并发集合类实现了这种机制。
代码示例(安全失败):
importjava.util.Iterator; importjava.util.concurrent.CopyOnWriteArrayList; publicclassFailSafeExample{ publicstaticvoidmain(String[] args){ CopyOnWriteArrayList<String> list =newCopyOnWriteArrayList<>(); list.add("A"); list.add("B"); list.add("C"); Iterator<String> iterator = list.iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); // 在迭代过程中尝试修改集合,不会抛出异常 list.add("D"); } } }
在这个例子中,尽管在遍历期间向CopyOnWriteArrayList添加了元素,迭代过程仍能顺利完成,不会抛出任何异常。这是因为CopyOnWriteArrayList在迭代时实际上是在其内部的一个副本上进行的,所以对原集合的修改不会影响迭代过程。
ArrayList 和 LinkedList 的区别是什么?
ArrayList 和 LinkedList 都是 Java 中实现 List 接口的集合类,但它们在内部结构、性能特性和适用场景上存在显著差异。
内部实现
- ArrayList:基于动态数组实现,意味着元素在内存中是连续存储的。它提供了快速的随机访问,但插入和删除(尤其是中间位置)操作较慢,因为可能需要移动后续元素。
- LinkedList:基于双向链表实现,每个元素都有一个指向前后元素的引用。这使得插入和删除操作非常高效(只需改变相邻节点的引用即可),但随机访问元素较慢,因为需要从头节点开始逐个遍历。
性能特点
- 查询:ArrayList 优于 LinkedList,因为可以直接通过索引定位元素(时间复杂度 O(1))。
- 插入/删除:
- 在列表开头或结尾,两者性能相近(时间复杂度均为 O(1))。
- 在列表中间,LinkedList 优于 ArrayList,因为 ArrayList 需要移动元素(时间复杂度接近 O(n)),而 LinkedList 只需调整几个引用(时间复杂度 O(1))。
代码示例
importjava.util.ArrayList; importjava.util.LinkedList; importjava.util.List; publicclassArrayListVsLinkedListExample{ publicstaticvoidmain(String[] args){ // ArrayList 示例 List<Integer> arrayList =newArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); System.out.println("ArrayList get(1): "+ arrayList.get(1));// 快速访问 arrayList.add(1,4);// 插入操作,可能需要移动元素 System.out.println("ArrayList after insert: "+ arrayList); // LinkedList 示例 List<Integer> linkedList =newLinkedList<>(); linkedList.add(1); linkedList.add(2); linkedList.add(3); System.out.println("LinkedList get(1): "+ linkedList.get(1));// 较慢的访问 linkedList.add(1,4);// 插入操作,非常高效 System.out.println("LinkedList after insert: "+ linkedList); } }
总结
选择 ArrayList 还是 LinkedList 应基于实际需求:
- 如果您需要频繁的随机访问元素,且集合大小相对稳定,ArrayList 是更好的选择。
- 如果您需要频繁地在集合中间插入或删除元素,LinkedList 会提供更好的性能。
如何决定使用 HashMap 还是 TreeMap?
在Java中,选择使用HashMap还是TreeMap取决于您的具体需求,主要考虑以下几个方面:
-
有序性:
- HashMap:不保证元素的顺序,插入和访问的顺序可能不一致。它根据键的哈希值存储元素,因此对于快速查找非常高效。
- TreeMap:按照键的自然顺序或者自定义比较器排序,保证了元素的有序性。这对于需要按顺序遍历或查找特定范围内的键值对的场景非常有用。
-
性能:
- HashMap:在平均情况下,插入、删除和查找操作的时间复杂度为O(1)。但在最坏情况下(哈希冲突严重),性能可能退化到O(n)。
- TreeMap:插入、删除和查找操作的时间复杂度均为O(log n),其中n是树中元素的数量。这比HashMap在最坏情况下的性能要好,但通常慢于HashMap的平均情况。
-
内存使用:
- HashMap:通常比TreeMap更节省内存,因为它不需要维护键之间的排序关系。
- TreeMap:由于内部实现为红黑树,需要额外的空间来维护树的结构,因此通常比HashMap占用更多内存。
代码示例
HashMap示例:适用于快速访问,不关心元素顺序的情况。
importjava.util.HashMap; publicclassHashMapExample{ publicstaticvoidmain(String[] args){ HashMap<String, Integer> map =newHashMap<>(); map.put("One",1); map.put("Two",2); map.put("Three",3); System.out.println("Value for key 'Two': "+ map.get("Two"));// 快速访问 } }
TreeMap示例:当需要按键的自然顺序或自定义顺序进行遍历时。
importjava.util.TreeMap; publicclassTreeMapExample{ publicstaticvoidmain(String[] args){ TreeMap<String, Integer> map =newTreeMap<>(); map.put("One",1); map.put("Two",2); map.put("Three",3); // 遍历按自然顺序排列的键值对 for(String key : map.keySet()) { System.out.println(key +": "+ map.get(key)); } } }
根据您的应用场景,如果需要快速随机访问且不关心顺序,选择HashMap;如果需要保持键的排序(自然顺序或自定义顺序),则应选择TreeMap。
Java中怎么让集合的值不能修改?
在Java中,创建不能修改的集合,通常指的是创建集合的不可变视图。主要有两种方式来实现这一目的:使用Collections.unmodifiableXXX()方法或者利用Java 9引入的静态工厂方法,如List.of()、Set.of()和Map.of()等。下面是这两种方式的代码示例:
使用Collections.unmodifiableXXX()方法
这种方式会将现有的集合转换为不可修改的集合视图。尝试修改这样的集合会抛出UnsupportedOperationException。
不可变List
importjava.util.Arrays; importjava.util.Collections; importjava.util.List; List<String> mutableList = Arrays.asList("a","b","c"); List<String> unmodifiableList = Collections.unmodifiableList(mutableList); // 尝试修改会抛出UnsupportedOperationException // unmodifiableList.add("d");
不可变Set
importjava.util.Arrays; importjava.util.Collections; importjava.util.Set; Set<String> mutableSet =newHashSet<>(Arrays.asList("a","b","c")); Set<String> unmodifiableSet = Collections.unmodifiableSet(mutableSet); // 尝试修改会抛出UnsupportedOperationException // unmodifiableSet.add("d");
不可变Map
importjava.util.Arrays; importjava.util.Collections; importjava.util.Map; Map<String, String> mutableMap =newHashMap<>(); mutableMap.put("key1","value1"); mutableMap.put("key2","value2"); Map<String, String> unmodifiableMap = Collections.unmodifiableMap(mutableMap); // 尝试修改会抛出UnsupportedOperationException // unmodifiableMap.put("key3", "value3");
使用Java 9的of方法
从Java 9开始,可以直接使用集合接口的静态工厂方法创建不可变集合,更加简洁。
不可变List
importjava.util.List; List<String> unmodifiableList = List.of("a","b","c"); // 尝试修改会抛出UnsupportedOperationException // unmodifiableList.add("d");
不可变Set
importjava.util.Set; Set<String> unmodifiableSet = Set.of("a","b","c"); // 尝试修改会抛出UnsupportedOperationException // unmodifiableSet.add("d");
不可变Map
importjava.util.Map; Map<String, String> unmodifiableMap = Map.of("key1","value1","key2","value2"); // 尝试修改会抛出UnsupportedOperationException // unmodifiableMap.put("key3", "value3");
以上两种方法都可以创建不可修改的集合,选择哪种方式取决于你使用的Java版本和个人偏好。使用Java 9及更高版本时,推荐使用of方法,因为它提供了更简洁的语法和明确的意图表达。
本文采摘于网络,不代表本站立场,转载联系作者并注明出处:https://www.iotsj.com//kuaixun/3258.html