Skip to content

Java集合框架面试题

1. 集合框架的基本概念

问题:什么是集合框架?

答案: 集合框架是Java中用于存储和操作一组对象的API,它提供了各种数据结构和算法,方便开发者处理数据。

2. 集合框架的层次结构

问题:Java集合框架的层次结构是怎样的?

答案

  • Collection接口:是所有集合的根接口,包含List、Set、Queue等子接口。
  • Map接口:是映射的根接口,存储键值对。
  • Iterator接口:用于遍历集合中的元素。

3. Collection接口的子接口

问题:Collection接口有哪些子接口?

答案

  • List:有序集合,允许重复元素。
  • Set:无序集合,不允许重复元素。
  • Queue:队列,用于存储和处理元素。
  • Deque:双端队列,支持两端操作。

4. List接口的实现类

问题:List接口有哪些实现类?它们的区别是什么?

答案

  • ArrayList:基于动态数组实现,查询快,插入和删除慢。
  • LinkedList:基于双向链表实现,查询慢,插入和删除快。
  • Vector:基于动态数组实现,线程安全,性能较差。
  • Stack:继承自Vector,实现栈数据结构。

5. Set接口的实现类

问题:Set接口有哪些实现类?它们的区别是什么?

答案

  • HashSet:基于哈希表实现,无序,不允许重复元素。
  • LinkedHashSet:基于哈希表和链表实现,有序(插入顺序),不允许重复元素。
  • TreeSet:基于红黑树实现,有序(自然排序),不允许重复元素。

6. Map接口的实现类

问题:Map接口有哪些实现类?它们的区别是什么?

答案

  • HashMap:基于哈希表实现,无序,允许null键和null值。
  • LinkedHashMap:基于哈希表和链表实现,有序(插入顺序),允许null键和null值。
  • TreeMap:基于红黑树实现,有序(自然排序),不允许null键。
  • Hashtable:基于哈希表实现,线程安全,不允许null键和null值。
  • ConcurrentHashMap:基于哈希表实现,线程安全,性能较好。

7. ArrayList和LinkedList的区别

问题:ArrayList和LinkedList的区别是什么?

答案

特性ArrayListLinkedList
底层实现动态数组双向链表
随机访问快(O(1))慢(O(n))
插入和删除慢(O(n))快(O(1))
内存占用较小较大(需要存储前后节点引用)
遍历效率

8. HashMap的工作原理

问题:HashMap的工作原理是什么?

答案

  • HashMap基于哈希表实现,使用数组存储键值对。
  • 通过key的hashCode()方法计算哈希值,然后对数组长度取模得到索引位置。
  • 如果发生哈希冲突,使用链表或红黑树存储冲突的元素。
  • Java 8中,当链表长度超过8时,会将链表转换为红黑树以提高查询效率。

9. HashSet的工作原理

问题:HashSet的工作原理是什么?

答案

  • HashSet基于HashMap实现,使用HashMap的key存储元素。
  • 当添加元素时,将元素作为key,null作为value存入HashMap。
  • 由于HashMap的key不允许重复,所以HashSet也不允许重复元素。

10. TreeSet的工作原理

问题:TreeSet的工作原理是什么?

答案

  • TreeSet基于TreeMap实现,使用TreeMap的key存储元素。
  • TreeMap基于红黑树实现,所以TreeSet中的元素是有序的。
  • 元素需要实现Comparable接口或在构造TreeSet时提供Comparator。

11. HashMap和Hashtable的区别

问题:HashMap和Hashtable的区别是什么?

答案

特性HashMapHashtable
线程安全非线程安全线程安全
null键值允许null键和null值不允许null键和null值
效率
继承关系继承自AbstractMap继承自Dictionary
遍历方式迭代器枚举器和迭代器

12. ConcurrentHashMap的工作原理

问题:ConcurrentHashMap的工作原理是什么?

答案

  • ConcurrentHashMap是线程安全的HashMap实现。
  • Java 7中,使用分段锁(Segment)实现并发控制。
  • Java 8中,使用CAS(Compare-And-Swap)操作和synchronized关键字实现并发控制,不再使用分段锁。
  • ConcurrentHashMap的性能比Hashtable好,因为它只锁定部分数据,而不是整个哈希表。

13. 集合的遍历方式

问题:集合有哪些遍历方式?

答案

  • 迭代器遍历:使用Iterator或ListIterator。
  • 增强for循环:使用for-each循环。
  • 普通for循环:使用索引遍历(适用于List)。
  • Stream API:使用Java 8的Stream API。

示例

java
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");

// 迭代器遍历
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

// 增强for循环
for (String s : list) {
    System.out.println(s);
}

// 普通for循环
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

// Stream API
list.stream().forEach(System.out::println);

14. 集合的排序

问题:如何对集合进行排序?

答案

  • List排序:使用Collections.sort()方法或List.sort()方法。
  • Set排序:使用TreeSet或转换为List后排序。
  • Map排序:根据key或value排序,转换为List后排序。

示例

java
// List排序
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(1);
list.add(2);
Collections.sort(list); // 升序
Collections.sort(list, Collections.reverseOrder()); // 降序

// Set排序
Set<Integer> set = new TreeSet<>();
set.add(3);
set.add(1);
set.add(2); // 自动排序

// Map排序
Map<String, Integer> map = new HashMap<>();
map.put("c", 3);
map.put("a", 1);
map.put("b", 2);

// 根据key排序
List<Map.Entry<String, Integer>> entries = new ArrayList<>(map.entrySet());
entries.sort(Map.Entry.comparingByKey());

// 根据value排序
entries.sort(Map.Entry.comparingByValue());

15. 集合的线程安全

问题:如何实现集合的线程安全?

答案

  • 使用线程安全的集合类:Vector、Hashtable、ConcurrentHashMap等。
  • 使用Collections.synchronizedXXX()方法包装非线程安全的集合。
  • 使用CopyOnWriteArrayList或CopyOnWriteArraySet。

示例

java
// 使用线程安全的集合类
List<String> vector = new Vector<>();
Map<String, String> hashtable = new Hashtable<>();
Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();

// 使用Collections.synchronizedXXX()方法
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
Set<String> synchronizedSet = Collections.synchronizedSet(new HashSet<>());
Map<String, String> synchronizedMap = Collections.synchronizedMap(new HashMap<>());

// 使用CopyOnWriteArrayList
List<String> copyOnWriteList = new CopyOnWriteArrayList<>();

16. 集合的扩容机制

问题:ArrayList和HashMap的扩容机制是什么?

答案

  • ArrayList

    • 初始容量为10。
    • 当容量不足时,新容量为原来的1.5倍。
    • 使用Arrays.copyOf()方法复制元素。
  • HashMap

    • 初始容量为16,加载因子为0.75。
    • 当元素数量超过容量*加载因子时,进行扩容。
    • 新容量为原来的2倍。
    • 重新计算所有元素的哈希值并重新分布。

17. 集合的空集合

问题:如何创建空集合?

答案

  • 空List:Collections.emptyList()
  • 空Set:Collections.emptySet()
  • 空Map:Collections.emptyMap()

示例

java
List<String> emptyList = Collections.emptyList();
Set<String> emptySet = Collections.emptySet();
Map<String, String> emptyMap = Collections.emptyMap();

18. 集合的不可变集合

问题:如何创建不可变集合?

答案

  • 使用Collections.unmodifiableXXX()方法

    java
    List<String> list = new ArrayList<>();
    list.add("a");
    List<String> unmodifiableList = Collections.unmodifiableList(list);
  • 使用Guava库

    java
    List<String> immutableList = ImmutableList.of("a", "b", "c");
  • 使用Java 9+的of()方法

    java
    List<String> immutableList = List.of("a", "b", "c");
    Set<String> immutableSet = Set.of("a", "b", "c");
    Map<String, String> immutableMap = Map.of("a", "1", "b", "2");

19. 集合的性能比较

问题:不同集合的性能比较?

答案

  • 查询速度:ArrayList > LinkedList,HashMap > TreeMap
  • 插入和删除速度:LinkedList > ArrayList,HashMap > TreeMap
  • 内存占用:ArrayList < LinkedList,HashMap < TreeMap
  • 线程安全:ConcurrentHashMap > Hashtable > 非线程安全集合

20. 集合的使用场景

问题:不同集合的使用场景?

答案

  • ArrayList:适用于查询频繁,插入和删除较少的场景。
  • LinkedList:适用于插入和删除频繁,查询较少的场景。
  • HashSet:适用于需要快速查找元素,且不允许重复的场景。
  • LinkedHashSet:适用于需要保持插入顺序的场景。
  • TreeSet:适用于需要排序的场景。
  • HashMap:适用于需要快速查找键值对的场景。
  • LinkedHashMap:适用于需要保持插入顺序的场景。
  • TreeMap:适用于需要按键排序的场景。
  • ConcurrentHashMap:适用于多线程环境。

21. 集合的序列化

问题:如何实现集合的序列化?

答案

  • 实现Serializable接口。
  • 使用ObjectOutputStream和ObjectInputStream进行序列化和反序列化。

示例

java
// 序列化
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");

try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.ser"))) {
    oos.writeObject(list);
}

// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.ser"))) {
    List<String> deserializedList = (List<String>) ois.readObject();
    System.out.println(deserializedList);
}

22. 集合的遍历注意事项

问题:遍历集合时需要注意什么?

答案

  • 并发修改异常:在遍历集合时,不要修改集合的结构(添加或删除元素),否则会抛出ConcurrentModificationException。
  • 使用迭代器:如果需要在遍历过程中修改集合,使用迭代器的remove()方法。

示例

java
// 错误的做法
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");

for (String s : list) {
    if (s.equals("b")) {
        list.remove(s); // 会抛出ConcurrentModificationException
    }
}

// 正确的做法
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String s = iterator.next();
    if (s.equals("b")) {
        iterator.remove(); // 正确
    }
}

23. 集合的工具类

问题:Java提供了哪些集合工具类?

答案

  • Collections:提供了排序、查找、替换等静态方法。
  • Arrays:提供了数组操作的静态方法。

示例

java
// Collections工具类
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(1);
list.add(2);

Collections.sort(list); // 排序
Collections.reverse(list); // 反转
Collections.shuffle(list); // 打乱
Collections.max(list); // 最大值
Collections.min(list); // 最小值

// Arrays工具类
int[] array = {3, 1, 2};
Arrays.sort(array); // 排序
Arrays.toString(array); // 转换为字符串
Arrays.copyOf(array, 5); // 复制数组

24. 集合的泛型

问题:什么是集合的泛型?有什么作用?

答案

  • 泛型:是一种参数化类型的机制,允许在定义类、接口和方法时使用类型参数。
  • 作用
    • 提高代码的安全性,避免类型转换错误。
    • 提高代码的可读性,明确集合中元素的类型。
    • 提高代码的重用性。

示例

java
// 使用泛型
List<String> list = new ArrayList<>();
list.add("a"); // 只能添加String类型
String s = list.get(0); // 不需要类型转换

// 不使用泛型
List list = new ArrayList();
list.add("a");
list.add(1); // 可以添加任意类型
String s = (String) list.get(0); // 需要类型转换

25. 集合的Stream API

问题:什么是Stream API?如何使用?

答案

  • Stream API:是Java 8引入的一种处理集合的新方式,提供了函数式编程的风格。
  • 使用方式

示例

java
List<String> list = Arrays.asList("a", "b", "c", "d");

// 过滤
List<String> filteredList = list.stream()
    .filter(s -> s.startsWith("a"))
    .collect(Collectors.toList());

// 映射
List<Integer> lengths = list.stream()
    .map(String::length)
    .collect(Collectors.toList());

// 排序
List<String> sortedList = list.stream()
    .sorted()
    .collect(Collectors.toList());

// 统计
long count = list.stream().count();
Optional<String> max = list.stream().max(String::compareTo);
Optional<String> min = list.stream().min(String::compareTo);