Java八股文——集合「概念篇」
数组与集合区别,用过哪些?
数组与集合的区别:
- 长度:数组是固定长度的数据结构,一旦创建其长度就不可改变。而集合是动态长度的数据结构,能够根据需要动态增加或删除元素。
- 数据类型:数组可以包含基本数据类型(如int、char等)和对象,而集合只能包含对象。
- 访问方式:数组可以通过索引直接访问元素,而集合通常需要通过迭代器或其他方法(如forEach、stream)来访问元素。
我常用的 Java 集合类:
- ArrayList:实现了List接口,底层使用动态数组,支持快速的随机访问和动态增长,适用于经常读取和少量插入删除的场景。
- LinkedList:实现了List接口,底层使用双向链表,适用于频繁的插入和删除操作,访问速度相对较慢,但在特定场景下表现更优。
- HashMap:基于哈希表实现的Map接口,存储键值对,支持通过键快速查找对应的值。适用于需要高效查找的场景。
- HashSet:基于HashMap实现的Set集合,用于存储唯一元素,插入和查找操作平均时间复杂度为O(1)。
- TreeMap:基于红黑树实现的有序Map集合,支持按照键的自然顺序或自定义顺序进行排序,适用于需要排序的场景。
- LinkedHashMap:结合了HashMap的哈希表和双向链表的特性,保持元素的插入顺序或访问顺序。适用于需要维护插入顺序的场景。
- PriorityQueue:基于堆实现的优先队列,元素按照自然顺序或指定的比较器进行排序,适用于需要按优先级顺序处理元素的场景。
说说Java中的集合?
Java 中集合的概念及常见类型:
Java中的集合:
在 Java 中,集合(Collection)是用来存储和操作数据的一种数据结构。集合类提供了许多灵活的数据存储方式,并能够执行各种操作,如插入、删除、查找和排序等。Java 的集合框架分为两大类:
- Collection 接口:
- 这是集合框架的根接口,表示所有集合的通用操作。Collection接口有多种实现,主要包括:
- List:有序、允许重复的集合。
- Set:无序、不允许重复的集合。
- Queue:通常用于存储等待处理的元素(如消息队列)。
- 这是集合框架的根接口,表示所有集合的通用操作。Collection接口有多种实现,主要包括:
- Map 接口:
- 用于存储键值对的数据结构。它不继承于 Collection 接口,但也属于集合框架的一部分。
常用的 Java 集合类:
- List 接口:
- ArrayList:基于动态数组实现,支持快速的随机访问,适用于需要频繁访问的场景。
- LinkedList:基于双向链表实现,适用于需要频繁插入和删除的场景,访问速度较慢,但插入和删除操作较快。
- Vector:与 ArrayList 类似,但它是线程安全的,性能较差。
- Set 接口:
- HashSet:基于哈希表实现,存储不重复的元素,插入和查找操作的时间复杂度通常为 O(1)。
- LinkedHashSet:继承自 HashSet,保持元素的插入顺序,适用于需要维护插入顺序的场景。
- TreeSet:基于红黑树实现,存储唯一的元素,按自然顺序或指定顺序进行排序。
- Queue 接口:
- PriorityQueue:优先队列,元素根据其优先级排序,可以自定义排序规则或使用元素的自然顺序。
- LinkedList:除了实现 List 接口,还实现了 Queue 接口,支持队列操作(如插入、删除等)。
- Map 接口:
- HashMap:基于哈希表实现,存储键值对,通过键进行快速查找。适用于不需要排序的场景。
- LinkedHashMap:基于哈希表和双向链表实现,保持元素的插入顺序或访问顺序。
- TreeMap:基于红黑树实现,按键的自然顺序或指定顺序进行排序。
- Hashtable:类似于 HashMap,但它是线程安全的,性能较差,已被 ConcurrentHashMap 替代。
集合的常见操作:
- 添加元素:add()、put()(对于 Map)等。
- 删除元素:remove()。
- 查找元素:contains()、get()(对于 Map)等。
- 遍历集合:使用迭代器(Iterator)或增强的 for 循环。
总结:
Java 集合框架非常强大,涵盖了各种数据结构和算法,能够满足不同场景下的数据存储和操作需求。根据具体的应用需求,选择合适的集合类能够提高代码的效率和可维护性。
Java中的线程安全的集合是什么?
Java中的线程安全的集合:
线程安全的集合类是指能够在多线程环境下安全地使用的集合类。这些集合类内部采取了措施,确保多个线程同时访问时不会出现数据不一致或并发修改的问题。Java 提供了多种线程安全的集合,常见的有:
1. Vector
- Vector 类实现了 List 接口,并且它是线程安全的。它通过对所有方法加锁(同步)来保证线程安全。
- 然而,由于 Vector 的同步机制,性能会比非线程安全的集合(如 ArrayList)差,通常不推荐使用。
- 使用场景:适用于少量线程并发访问的情况,但不推荐在高并发环境中使用。
2. Hashtable
- Hashtable 是一个线程安全的 Map 实现类,内部的每个方法都使用 synchronized 关键字进行同步,确保在多线程环境下安全访问。
- 然而,Hashtable 的性能较差,且它不允许 null 键或值,因此在新的项目中更推荐使用 ConcurrentHashMap 代替它。
- 使用场景:适用于需要线程安全的 Map 实现,但在现代 Java 开发中已被淘汰。
3. CopyOnWriteArrayList
- CopyOnWriteArrayList 是一个线程安全的 List 实现,适用于读取多、写入少的场景。
- 它通过每次修改时创建一个副本的方式来保证线程安全,避免了同步带来的性能瓶颈。
- 使用场景:在多线程环境中,如果集合修改较少且读取频繁,可以使用 CopyOnWriteArrayList。
4. CopyOnWriteArraySet
- CopyOnWriteArraySet 是一个线程安全的 Set 实现,底层使用 CopyOnWriteArrayList 实现,因此同样适用于读多写少的场景。
- 使用场景:适用于需要线程安全且修改频率较低的集合。
5. ConcurrentHashMap
- ConcurrentHashMap 是一个线程安全的 Map 实现,它比 Hashtable 更高效。它采用分段锁的机制,将锁粒度从整体 Map 降到每个分段,提高了并发性能。
- 使用场景:适用于高并发环境,特别是在需要高效并发访问的情况下。
6. BlockingQueue 接口及其实现
- BlockingQueue 是一个线程安全的队列接口,通常用于在生产者-消费者模式中协调线程之间的工作。
- 常见实现包括:
- ArrayBlockingQueue:基于数组实现的有界阻塞队列。
- LinkedBlockingQueue:基于链表实现的阻塞队列,支持可扩展的大小。
- PriorityBlockingQueue:优先级阻塞队列,按优先级顺序处理元素。
- 使用场景:适用于需要在多线程间安全传递数据的场景,如消息队列、任务调度等。
总结:
在多线程环境下,选择线程安全的集合类可以避免并发操作时出现的问题。常用的线程安全集合类包括:
- Vector 和 Hashtable(老旧的线程安全实现);
- CopyOnWriteArrayList 和 CopyOnWriteArraySet(适用于读多写少的场景);
- ConcurrentHashMap(适用于高并发环境);
- BlockingQueue 接口及其实现类(用于生产者消费者模式)。
根据具体的使用场景,选择合适的线程安全集合类能够提高程序的性能和稳定性。
Collections和Collection的区别
- Collection:
- Collection 是 Java 集合框架中的一个接口,位于 java.util 包中。它是所有集合类的基础接口,定义了一组通用的操作和方法,用于管理和操作一组对象。
- Collection 接口包含常用的集合操作方法,如添加、删除、遍历等,主要用于描述集合的基本行为。
- Collection 接口有多个常见的实现类,如:
- List:有序集合,允许元素重复。
- Set:无序集合,不允许重复元素。
- Queue:队列,通常用于存储等待处理的元素。
- 总结:Collection 是集合类的基类,它提供了集合操作的通用接口。
- Collections:
- Collections 是一个 Java 提供的工具类,位于 java.util 包中,提供了一系列静态方法用于对集合进行操作和算法。
- Collections 类中的方法包括:
- 排序:sort(),用于对 List 进行排序。
- 查找:binarySearch(),用于在已排序的 List 中查找元素。
- 替换:replaceAll(),用于替换集合中的元素。
- 反转:reverse(),用于反转集合中的元素顺序。
- 随机化:shuffle(),用于打乱集合中元素的顺序。
- Collections 类中的方法是静态的,且适用于实现了 Collection 接口的集合,如 List 和 Set。
- 总结:Collections 提供的是对集合的操作工具类,封装了许多常用的集合操作方法。
总结:
- Collection 是集合框架中的一个接口,定义了集合的基本行为和操作方法。
- Collections 是一个工具类,提供了静态方法用于对实现了 Collection 接口的集合进行各种操作,如排序、查找、替换等。
集合遍历的方法有哪些?
Java 提供了多种方式来遍历集合,不同的遍历方法适用于不同的场景。常见的遍历方法包括:
1. 使用增强 for 循环(for-each)
- 适用场景:适用于任何实现了 Iterable 接口的集合(如 List、Set 等)。这种方式简洁易懂,不需要手动管理索引。
- 示例:
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");for (String item : list) {System.out.println(item);
}
- 优点:代码简洁,适用于元素不需要修改的场景。
2. 使用传统的 for 循环(通过索引)
- 适用场景:适用于 List 等有索引的集合,尤其是当你需要根据索引进行某些操作时。
- 示例:
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));
}
- 优点:当需要访问集合的元素的索引或修改元素时非常方便。
3. 使用 Iterator
- 适用场景:适用于任何实现了 Collection 接口的集合,特别是在需要进行删除操作时。Iterator 是一种通用的遍历方式,能够避免并发修改异常。
- 示例:
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());
}
- 优点:可以安全地进行元素的删除操作,并避免使用索引时可能出现的并发修改问题。
4.使用 forEach 方法(Java 8 引入)
- 适用场景:适用于所有实现了 Iterable 接口的集合。forEach 是 Java 8 引入的函数式编程风格,可以接受 Lambda 表达式或方法引用作为参数。
- 示例:
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");list.forEach(item -> System.out.println(item));
- 优点:代码更加简洁,符合函数式编程风格。如果集合较大,forEach 与流(Stream)结合使用时,可以进行更灵活的处理。
5. 使用 ListIterator(仅适用于 List)
- 适用场景:适用于 List 集合,ListIterator 是 Iterator 的一个增强版本,支持双向遍历,并能够在遍历过程中进行修改操作。
- 示例:
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {System.out.println(listIterator.next());
}
- 优点:可以双向遍历,还可以使用 set() 方法修改当前元素。
6. 使用 Stream(Java 8 引入)
- 适用场景:适用于 Java 8 引入的 Stream API,能够以声明式方式进行集合的遍历和操作,适合复杂操作的场景。
- 示例:
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");list.stream().forEach(System.out::println);
- 优点:与函数式编程风格兼容,能够链式调用流的操作,适用于复杂的数据处理任务。
总结:
- 增强 for 循环:简洁,适合只读操作。
- 传统 for 循环:适合需要根据索引操作的场景。
- Iterator:适合需要删除元素或避免并发修改异常的场景。
- forEach:Java 8 引入,函数式编程风格,简洁并支持 Lambda。
- ListIterator:适用于 List,支持双向遍历和修改。
- Stream:适用于复杂操作,特别是在 Java 8 中,提供了更灵活的操作方式。
根据具体需求选择合适的遍历方法,有助于提高代码的简洁性、可读性和执行效率。
参考小林coding和JavaGuide