1.集合容器框架 - Java开发中(非线程安全)集合容器相关API及其最佳实践

一、Java开发中(非线程安全)集合容器相关API及其最佳实践

一、Java开发中(非线程安全)集合容器相关API及其最佳实践

注意:以下的所有这些集合框架都不是线程安全的,需要使用 Collections.synchronizedXXX 方法或 Concurrent 包中的类来实现线程安全。

对于容器(集合)的讲解

在Java开发中,容器(Collections)是存储和操作数据的基本工具之一。使用容器的最佳实践能够极大地提高代码的性能、可读性和维护性。下面,将详细讲解Java开发中使用容器的最佳实践。

1. 容器的基本概念

定义

容器是用来存储和操作数据对象的集合类。Java提供了一系列的容器类和接口,它们位于 java.util 包中,主要包括 List, Set, Map 等。

分类

容器主要分为三类:

  • List: 有序集合,可以包含重复元素。例如,ArrayList, LinkedList.
  • Set: 无序集合,不包含重复元素。例如,HashSet, TreeSet.
  • Map: 键值对集合,不包含重复键。例如,HashMap, TreeMap.

2. 常用容器及其使用场景

List
  • ArrayList 适用于频繁读取数据的场景,因为其基于数组实现,访问速度快。但插入和删除操作较慢,因为需要移动元素。
List<String> arrayList = new ArrayList<>();
arrayList.add("Apple");
arrayList.add("Banana");
  • LinkedList 适用于频繁插入和删除操作的场景,因为其基于链表实现,插入和删除速度快,但访问速度较慢。
List<String> linkedList = new LinkedList<>();
linkedList.add("Cherry");
linkedList.add("Date");
Set
  • HashSet 适用于需要快速查找的场景,因为其基于哈希表实现,查找速度快。但不保证顺序。
Set<String> hashSet = new HashSet<>();
hashSet.add("Elephant");
hashSet.add("Frog");
  • TreeSet 适用于需要排序的场景,因为其基于红黑树实现,能够对元素进行排序。
Set<String> treeSet = new TreeSet<>();
treeSet.add("Giraffe");
treeSet.add("Horse");
Map
  • HashMap 适用于需要快速查找键值对的场景,因为其基于哈希表实现,查找速度快。但不保证顺序。
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("Key1", 1);
hashMap.put("Key2", 2);
  • TreeMap 适用于需要按键排序的场景,因为其基于红黑树实现,能够对键进行排序。
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("KeyA", 100);
treeMap.put("KeyB", 200);

3. 容器使用的最佳实践

选择合适的容器

根据具体的使用场景选择合适的容器类型。例如,如果需要频繁进行插入和删除操作,可以选择LinkedList;如果需要快速查找,可以选择HashMapHashSet

使用泛型(Generics)

使用泛型可以确保类型安全,避免在运行时发生 ClassCastException

List<String> list = new ArrayList<>();
list.add("Apple");
// list.add(123);  // 编译错误
避免使用过时的容器类

使用现代的集合框架类(如ArrayListHashMap等),避免使用过时的类(如VectorHashtable等)。

使用接口进行编程

尽量使用接口类型(如ListMap等)进行声明,而不是具体实现类(如ArrayListHashMap等)。

List<String> list = new ArrayList<>();
使用 Collections 工具类

Java提供了Collections工具类,包含了各种操作集合的方法,如排序、查找、线程安全处理等。

Collections.sort(list);
合理设置初始容量

在创建集合时,合理设置初始容量可以提高性能,避免频繁扩容带来的开销。

Map<String, Integer> map = new HashMap<>(16);
使用线程安全的集合

在多线程环境中,使用线程安全的集合类,如ConcurrentHashMapCopyOnWriteArrayList等。

Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
List<String> copyOnWriteList = new CopyOnWriteArrayList<>();
使用不可变集合

在需要只读集合的场景下,使用不可变集合可以提高安全性和性能。

List<String> immutableList = Collections.unmodifiableList(new ArrayList<>(list));
避免集合中的空值

尽量避免在集合中存储空值,尤其是在Map中,这样可以避免NullPointerException和其他潜在问题。

map.put("Key1", null);  // 避免这种情况

4. 容器的高级用法

自定义比较器

对于需要自定义排序的场景,可以实现Comparator接口。

class CustomComparator implements Comparator<String> {
    @Override
    public int compare(String o1, String o2) {
        return o1.length() - o2.length();
    }
}

List<String> customList = new ArrayList<>(Arrays.asList("apple", "banana", "cherry"));
Collections.sort(customList, new CustomComparator());
使用流(Stream)进行操作

Java 8引入了Stream API,使得对集合的操作更加简洁和高效。

List<String> fruits = Arrays.asList("apple", "banana", "cherry");
List<String> filteredFruits = fruits.stream()
                                    .filter(fruit -> fruit.startsWith("a"))
                                    .collect(Collectors.toList());
使用 Optional 避免空指针异常

在需要处理可能为空的值时,使用Optional可以提高代码的健壮性。

Optional<String> fruit = fruits.stream().filter(f -> f.startsWith("a")).findFirst();
fruit.ifPresent(System.out::println);

5. 性能优化

避免频繁扩容

在初始化集合时,尽量预估需要存储的元素数量,合理设置初始容量,避免频繁扩容带来的性能损耗。

使用适当的数据结构

选择合适的数据结构,以提高操作的性能。例如,如果需要频繁进行插入和删除操作,可以选择LinkedList;如果需要快速查找,可以选择HashMapTreeSet

减少不必要的同步

在单线程环境中,避免使用线程安全的集合类(如VectorHashtable等),因为它们会带来额外的同步开销。

6. 结论

容器在Java开发中扮演着至关重要的角色,通过掌握和实践容器的最佳实践,可以编写出高效、可维护和健壮的代码。

(一)详细分析和比较 Java 集合框架,包括它们的特点、优势、劣势、性质和本质等方面

有关的分析内容都汇聚在该表格中

为了便于理解和比较这些 Java 集合框架,我将接下来的有关的分析内容都汇聚在一个详细的表格中。这将帮助你更直观地理解和熟练运用这些集合框架。

集合框架实现方式特点优势劣势有序性线程安全性本质常见使用场景
ArrayList动态数组支持随机访问随机访问快,尾部操作快中间插入、删除慢,需要移动元素有序非线程安全动态数组随机访问频繁,尾部操作频繁
LinkedList双向链表高效的插入和删除插入、删除快,不移动元素随机访问慢有序非线程安全双向链表插入、删除频繁
HashMap哈希表支持快速查找、插入和删除查找、插入、删除快无序无序非线程安全哈希表快速查找
TreeMap红黑树有序键值对存储有序,支持范围查询插入、删除相对慢有序非线程安全红黑树有序查找,范围查询
LinkedHashMap哈希表 + 双向链表支持插入顺序或访问顺序有序,查找、插入、删除快比 HashMap 占用更多内存有序非线程安全哈希表 + 双向链表插入顺序,访问顺序,缓存实现
HashSet哈希表支持快速查找、插入和删除查找、插入、删除快无序无序非线程安全哈希表快速查找,不允许重复元素
TreeSet红黑树有序元素存储有序,支持范围查询插入、删除相对慢有序非线程安全红黑树有序查找,不允许重复元素
LinkedHashSet哈希表 + 双向链表支持插入顺序有序,查找、插入、删除快比 HashSet 占用更多内存有序非线程安全哈希表 + 双向链表插入顺序,不允许重复元素
PriorityQueue支持优先级排序优先级排序,高效插入、删除不支持随机访问无序非线程安全优先级排序,高效处理任务
ArrayDeque动态数组支持双端队列操作双端操作快,高效插入、删除中间插入、删除慢,需要移动元素有序非线程安全动态数组双端队列操作

下面将详细分析和比较这些 Java 集合框架,包括它们的特点、优势、劣势、性质和本质以及最佳实践的选择等。

比较总结

  • 有序性ArrayList, LinkedList, TreeMap, TreeSet, LinkedHashMap, LinkedHashSet, ArrayDeque 是有序的;HashMap, HashSet, PriorityQueue 是无序的。
  • 随机访问ArrayList, HashMap, TreeMap, LinkedHashMap, HashSet, TreeSet, LinkedHashSet, PriorityQueue 支持随机访问;LinkedList, ArrayDeque 不支持随机访问。
  • 插入和删除LinkedList, LinkedHashMap, LinkedHashSet, ArrayDeque 插入和删除快;ArrayList, TreeMap, TreeSet, PriorityQueue 插入和删除相对慢。
  • 内存占用LinkedHashMap, LinkedHashSet 比对应的 HashMap, HashSet 占用更多内存。
  • 线程安全:所有这些集合框架都不是线程安全的,需要使用 Collections.synchronizedXXX 方法或 Concurrent 包中的类来实现线程安全。

最佳实践

  • 随机访问频繁:使用 ArrayList
  • 插入和删除频繁:使用 LinkedList
  • 快速查找:使用 HashMap
  • 有序查找:使用 TreeMap
  • 插入顺序:使用 LinkedHashMapLinkedHashSet
  • 优先级排序:使用 PriorityQueue
  • 双端队列:使用 ArrayDeque

1. ArrayList

特点:基于动态数组实现,支持随机访问。
优势:随机访问快,尾部插入和删除快。
劣势:中间插入和删除慢,需要移动元素。
性质:有序,非线程安全。
本质:动态数组。

2. LinkedList

特点:基于双向链表实现,支持高效的插入和删除。
优势:插入和删除快,不需要移动元素。
劣势:随机访问慢,需要遍历链表。
性质:有序,非线程安全。
本质:双向链表。

3. HashMap

特点:基于哈希表实现,支持快速的查找、插入和删除。
优势:查找、插入和删除快。
劣势:无序,不保证元素的顺序。
性质:无序,非线程安全。
本质:哈希表。

4. TreeMap

特点:基于红黑树实现,支持有序的键值对存储。
优势:有序,支持范围查询。
劣势:插入和删除相对慢。
性质:有序,非线程安全。
本质:红黑树。

5. LinkedHashMap

特点:基于哈希表和双向链表实现,支持插入顺序或访问顺序。
优势:有序,查找、插入和删除快。
劣势:比 HashMap 占用更多内存。
性质:有序,非线程安全。
本质:哈希表 + 双向链表。

6. HashSet

特点:基于哈希表实现,支持快速的查找、插入和删除。
优势:查找、插入和删除快。
劣势:无序,不保证元素的顺序。
性质:无序,非线程安全。
本质:哈希表。

7. TreeSet

特点:基于红黑树实现,支持有序的元素存储。
优势:有序,支持范围查询。
劣势:插入和删除相对慢。
性质:有序,非线程安全。
本质:红黑树。

8. LinkedHashSet

特点:基于哈希表和双向链表实现,支持插入顺序。
优势:有序,查找、插入和删除快。
劣势:比 HashSet 占用更多内存。
性质:有序,非线程安全。
本质:哈希表 + 双向链表。

9. PriorityQueue

特点:基于堆实现,支持优先级排序。
优势:优先级排序,高效的插入和删除。
劣势:不支持随机访问。
性质:无序,非线程安全。
本质:堆。

10. ArrayDeque

特点:基于动态数组实现,支持双端队列操作。
优势:双端操作快,高效的插入和删除。
劣势:中间插入和删除慢,需要移动元素。
性质:有序,非线程安全。
本质:动态数组。

  • 有序性ArrayList, LinkedList, TreeMap, TreeSet, LinkedHashMap, LinkedHashSet, ArrayDeque 是有序的;HashMap, HashSet, PriorityQueue 是无序的。
  • 随机访问ArrayList, HashMap, TreeMap, LinkedHashMap, HashSet, TreeSet, LinkedHashSet, PriorityQueue 支持随机访问;LinkedList, ArrayDeque 不支持随机访问。
  • 插入和删除LinkedList, LinkedHashMap, LinkedHashSet, ArrayDeque 插入和删除快;ArrayList, TreeMap, TreeSet, PriorityQueue 插入和删除相对慢。
  • 内存占用LinkedHashMap, LinkedHashSet 比对应的 HashMap, HashSet 占用更多内存。
  • 线程安全:所有这些集合框架都不是线程安全的,需要使用 Collections.synchronizedXXX 方法或 Concurrent 包中的类来实现线程安全。

(二)ArrayList

Java ArrayList 详解

ArrayList是Java集合框架中的一个重要类,它是一种基于动态数组的数据结构。相比于传统固定大小的数组,ArrayList在内存分配和管理上更为灵活。下面将详细介绍ArrayList的使用、内部原理及最佳实践。

1. ArrayList 基本概念
定义

ArrayList是Java集合框架中的类,位于java.util包中,它实现了List接口。ArrayList是一个可调整大小的数组实现,允许所有元素(包括null)。

主要特性
  • 动态数组:可以自动调整大小。
  • 有序:维护元素的插入顺序。
  • 允许重复:可以包含重复的元素。
  • 随机访问:基于索引的随机访问性能优越。
  • 线程不安全ArrayList不是线程安全的,适用于单线程环境。
2. ArrayList 的创建与初始化
2.1 无参构造函数

默认初始化一个空的ArrayList

ArrayList<String> list = new ArrayList<>();
2.2 指定初始容量

初始化一个具有指定初始容量的ArrayList

ArrayList<String> list = new ArrayList<>(10);
2.3 使用已有集合

通过已有集合初始化一个ArrayList

List<String> existingList = Arrays.asList("a", "b", "c");
ArrayList<String> list = new ArrayList<>(existingList);
3. ArrayList 的基本操作
3.1 添加元素
  • add:在列表末尾添加元素。
list.add("Hello");
list.add("World");
  • add:在指定索引位置添加元素。
list.add(1, "Java");
3.2 获取元素
  • get:通过索引获取元素。
String value = list.get(0);  // "Hello"
3.3 修改元素
  • set:替换指定索引位置的元素。
list.set(1, "Python");  // 将索引1处的元素替换为"Python"
3.4 删除元素
  • remove:通过索引删除元素。
list.remove(1);  // 删除索引1处的元素
  • remove:通过元素删除元素。
list.remove("Python");  // 删除值为"Python"的元素
3.5 检查元素
  • contains:检查列表中是否包含某个元素。
boolean containsHello = list.contains("Hello");  // true
  • indexOf:返回指定元素第一次出现的索引。
int index = list.indexOf("Hello");  // 0
3.6 遍历列表
  • 传统for循环
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}
  • 增强型for循环
for (String s : list) {
    System.out.println(s);
}
  • 使用Iterator
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
3.7 大小和清空
  • size:获取列表的大小。
int size = list.size();
  • isEmpty:检查列表是否为空。
boolean isEmpty = list.isEmpty();  // false
  • clear:清空列表。
list.clear();
4. ArrayList 的高级操作
4.1 批量操作
  • addAll:将一个集合中的所有元素添加到列表中。
List<String> newElements = Arrays.asList("a", "b", "c");
list.addAll(newElements);
  • removeAll:移除列表中存在于指定集合中的所有元素。
list.removeAll(newElements);
  • retainAll:保留列表中存在于指定集合中的所有元素。
list.retainAll(newElements);
4.2 子列表
  • subList:获取列表的一个子部分。
List<String> sublist = list.subList(0, 2);
4.3 并发修改

ArrayList是非线程安全的,如果在多线程环境中使用,可能导致并发修改异常。可以通过以下方式解决:

  • 使用同步
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
  • 使用并发集合
List<String> concurrentList = new CopyOnWriteArrayList<>();
5. ArrayList 的内存管理与性能
5.1 动态扩容机制
  • ArrayList在默认情况下初始容量为10。
  • 当添加元素超过当前容量时,ArrayList会自动扩容,通常为原容量的1.5倍。
  • 频繁的扩容操作会影响性能,因此在已知大致容量时,可以通过构造函数指定初始容量。
5.2 性能特点
  • 随机访问快:基于数组实现,支持O(1)的随机访问。
  • 插入和删除慢:插入和删除操作需要移动元素(平均O(n))。
  • 空间浪费:可能会有未使用的预分配空间。
6. ArrayList 的使用示例
6.1 基本使用
public class ArrayListExample {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("World");
        list.add(1, "Java");

        for (String s : list) {
            System.out.println(s);
        }

        if (list.contains("World")) {
            list.remove("World");
        }

        System.out.println("List size: " + list.size());
    }
}
6.2 实现自定义排序
public class ArrayListSorting {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Banana");
        list.add("Apple");
        list.add("Cherry");

        Collections.sort(list);
        System.out.println("Sorted list: " + list);

        Collections.sort(list, Collections.reverseOrder());
        System.out.println("Reverse sorted list: " + list);
    }
}
6.3 与其他集合转换
public class ArrayListConversion {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));

        // 转换为数组
        String[] array = list.toArray(new String[0]);
        System.out.println("Array: " + Arrays.toString(array));

        // 转换为其他集合
        Set<String> set = new HashSet<>(list);
        System.out.println("Set: " + set);
    }
}
7. ArrayList 的最佳实践
  • 避免频繁扩容:在知道大致元素数量时,指定初始容量。
  • 使用泛型:确保类型安全,避免类型转换异常。
  • 线程安全:在多线程环境中使用同步列表或并发集合。
  • 适当选择数据结构:根据需求选择合适的数据结构,ArrayList适用于随机访问频繁且插入删除较少的场景。

结论

ArrayList是Java中一个常用且强大的集合类,它提供了灵活的动态数组实现,适用于各种场景。通过掌握ArrayList的创建、基本操作、高级操作以及最佳实践,能够写出高效且易维护的代码。

(三)LinkedList

Java LinkedList 详解

LinkedList是Java集合框架中的一个重要类,它实现了ListDeque接口,是一种基于双向链表的数据结构。与ArrayList相比,LinkedList在插入和删除操作上更为高效,但在随机访问方面则稍逊一筹。下面将详细介绍LinkedList的使用、内部原理及最佳实践。

1. LinkedList 基本概念
定义

LinkedList是Java集合框架中的类,位于java.util包中,它实现了ListDeque接口。LinkedList是一个双向链表实现,允许所有元素(包括null)。

主要特性
  • 双向链表:每个元素都包含前驱和后继节点的引用。
  • 有序:维护元素的插入顺序。
  • 允许重复:可以包含重复的元素。
  • 随机访问慢:基于链表实现,随机访问性能较差。
  • 插入和删除快:在链表头尾或中间插入和删除元素性能优越。
  • 线程不安全LinkedList不是线程安全的,适用于单线程环境。
2. LinkedList 的创建与初始化
2.1 无参构造函数

默认初始化一个空的LinkedList

LinkedList<String> list = new LinkedList<>();
2.2 使用已有集合

通过已有集合初始化一个LinkedList

List<String> existingList = Arrays.asList("a", "b", "c");
LinkedList<String> list = new LinkedList<>(existingList);
3. LinkedList 的基本操作
3.1 添加元素
  • add:在列表末尾添加元素。
list.add("Hello");
list.add("World");
  • add:在指定索引位置添加元素。
list.add(1, "Java");
  • addFirst:在列表头部添加元素。
list.addFirst("First");
  • addLast:在列表尾部添加元素。
list.addLast("Last");
3.2 获取元素
  • get:通过索引获取元素。
String value = list.get(0);  // "First"
  • getFirst:获取列表头部元素。
String first = list.getFirst();  // "First"
  • getLast:获取列表尾部元素。
String last = list.getLast();  // "Last"
3.3 修改元素
  • set:替换指定索引位置的元素。
list.set(1, "Python");  // 将索引1处的元素替换为"Python"
3.4 删除元素
  • remove:通过索引删除元素。
list.remove(1);  // 删除索引1处的元素
  • remove:通过元素删除元素。
list.remove("Python");  // 删除值为"Python"的元素
  • removeFirst:删除列表头部元素。
list.removeFirst();  // 删除头部元素
  • removeLast:删除列表尾部元素。
list.removeLast();  // 删除尾部元素
3.5 检查元素
  • contains:检查列表中是否包含某个元素。
boolean containsHello = list.contains("Hello");  // true
  • indexOf:返回指定元素第一次出现的索引。
int index = list.indexOf("Hello");  // 0
3.6 遍历列表
  • 传统for循环
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}
  • 增强型for循环
for (String s : list) {
    System.out.println(s);
}
  • 使用Iterator
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
3.7 大小和清空
  • size:获取列表的大小。
int size = list.size();
  • isEmpty:检查列表是否为空。
boolean isEmpty = list.isEmpty();  // false
  • clear:清空列表。
list.clear();
4. LinkedList 的高级操作
4.1 队列操作

LinkedList实现了Deque接口,因此可以作为队列或双端队列使用。

  • offer:在队列尾部添加元素。
list.offer("QueueElement");
  • poll:移除并返回队列头部元素。
String head = list.poll();
  • peek:返回队列头部元素,但不移除。
String head = list.peek();
4.2 栈操作

LinkedList也可以作为栈使用。

  • push:在栈顶添加元素。
list.push("StackElement");
  • pop:移除并返回栈顶元素。
String top = list.pop();
  • peek:返回栈顶元素,但不移除。
String top = list.peek();
5. LinkedList 的内存管理与性能
5.1 内存结构
  • LinkedList的每个元素(节点)都包含前驱和后继节点的引用,因此每个节点占用更多的内存空间。
  • 由于是链表结构,LinkedList不需要预分配空间,因此内存使用更为灵活。
5.2 性能特点
  • 插入和删除快:在链表头尾或中间插入和删除元素性能优越,时间复杂度为O(1)。
  • 随机访问慢:基于链表实现,随机访问性能较差,时间复杂度为O(n)。
  • 空间灵活:不需要预分配空间,内存使用更为灵活。
6. LinkedList 的使用示例
6.1 基本使用
public class LinkedListExample {
    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<>();
        list.add("Hello");
        list.add("World");
        list.add(1, "Java");

        for (String s : list) {
            System.out.println(s);
        }

        if (list.contains("World")) {
            list.remove("World");
        }

        System.out.println("List size: " + list.size());
    }
}
6.2 实现队列
public class LinkedListQueue {
    public static void main(String[] args) {
        LinkedList<String> queue = new LinkedList<>();
        queue.offer("First");
        queue.offer("Second");
        queue.offer("Third");

        while (!queue.isEmpty()) {
            System.out.println(queue.poll());
        }
    }
}
6.3 实现栈
public class LinkedListStack {
    public static void main(String[] args) {
        LinkedList<String> stack = new LinkedList<>();
        stack.push("First");
        stack.push("Second");
        stack.push("Third");

        while (!stack.isEmpty()) {
            System.out.println(stack.pop());
        }
    }
}
7. LinkedList 的最佳实践
  • 避免频繁随机访问:由于LinkedList随机访问性能较差,尽量避免频繁的随机访问操作。
  • 使用泛型:确保类型安全,避免类型转换异常。
  • 线程安全:在多线程环境中使用同步列表或并发集合。
  • 适当选择数据结构:根据需求选择合适的数据结构,LinkedList适用于插入和删除频繁且随机访问较少的场景。

结论

LinkedList是Java中一个常用且强大的集合类,它提供了灵活的双向链表实现,适用于各种场景。通过掌握LinkedList的创建、基本操作、高级操作以及最佳实践,能够写出高效且易维护的代码。

(四)HashMap

Java HashMap 详解

HashMap是Java集合框架中的一个重要类,它基于哈希表实现,是一种用于存储键值对的哈希映射。HashMap提供了高效的插入、删除和查找操作。下面将详细介绍HashMap的使用、内部原理及最佳实践。

1. HashMap 基本概念
定义

HashMap是Java集合框架中的类,位于java.util包中,它实现了Map接口。HashMap基于哈希表实现,是一个用于存储键值对的哈希映射。

主要特性
  • 键值对存储:每个元素存储为一个键值对。
  • 无序:不保证元素的插入顺序。
  • 允许null键和值:最多允许一个null键和多个null值。
  • 基于哈希表:提供高效的插入、删除和查找操作。
  • 线程不安全HashMap不是线程安全的,适用于单线程环境。
2. HashMap 的创建与初始化
2.1 无参构造函数

默认初始化一个空的HashMap

HashMap<String, Integer> map = new HashMap<>();
2.2 指定初始容量和加载因子

可以指定初始容量和加载因子来初始化一个HashMap

HashMap<String, Integer> map = new HashMap<>(16, 0.75f);
2.3 使用已有映射

通过已有映射初始化一个HashMap

Map<String, Integer> existingMap = new HashMap<>();
existingMap.put("a", 1);
existingMap.put("b", 2);
HashMap<String, Integer> map = new HashMap<>(existingMap);
3. HashMap 的基本操作
3.1 添加和替换元素
  • put:向映射中添加键值对,如果键已存在,则替换其对应的值。
map.put("Hello", 1);
map.put("World", 2);
map.put("Hello", 3);  // 替换 "Hello" 的值为 3
  • putIfAbsent:仅当键不存在时,才向映射中添加键值对。
map.putIfAbsent("Java", 4);
3.2 获取元素
  • get:通过键获取对应的值,如果键不存在则返回null
Integer value = map.get("Hello");  // 3
  • getOrDefault:通过键获取对应的值,如果键不存在则返回指定的默认值。
Integer value = map.getOrDefault("Python", 0);  // 0
3.3 检查元素
  • containsKey:检查映射中是否包含指定的键。
boolean containsHello = map.containsKey("Hello");  // true
  • containsValue:检查映射中是否包含指定的值。
boolean containsValue = map.containsValue(3);  // true
3.4 删除元素
  • remove:通过键删除对应的键值对。
map.remove("World");
  • remove:通过键和值删除对应的键值对,仅当键和值同时匹配时才删除。
map.remove("Hello", 3);  // 仅当 "Hello" 对应的值为 3 时删除
3.5 大小和清空
  • size:获取映射的大小。
int size = map.size();
  • isEmpty:检查映射是否为空。
boolean isEmpty = map.isEmpty();  // false
  • clear:清空映射。
map.clear();
3.6 遍历映射
  • 遍历键值对
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
  • 遍历键
for (String key : map.keySet()) {
    System.out.println("Key: " + key);
}
  • 遍历值
for (Integer value : map.values()) {
    System.out.println("Value: " + value);
}
4. HashMap 的高级操作
4.1 批量操作
  • putAll:将一个映射中的所有键值对添加到HashMap中。
Map<String, Integer> newEntries = new HashMap<>();
newEntries.put("Python", 5);
newEntries.put("JavaScript", 6);
map.putAll(newEntries);
4.2 计算操作
  • compute:对指定的键进行重新计算并更新其值。
map.compute("Hello", (key, value) -> (value == null) ? 1 : value + 1);
  • computeIfAbsent:仅当键不存在时进行计算并更新其值。
map.computeIfAbsent("Ruby", key -> 7);
  • computeIfPresent:仅当键存在时进行计算并更新其值。
map.computeIfPresent("Java", (key, value) -> value + 1);
4.3 合并操作
  • merge:如果键不存在,则添加键值对;如果键存在,则对值进行合并。
map.merge("Java", 1, Integer::sum);
4.4 替换操作
  • replace:替换指定键的值,仅当键存在时才替换。
map.replace("Python", 8);
  • replace:仅当键和值同时匹配时才替换。
map.replace("JavaScript", 6, 9);
5. 内部原理与性能
5.1 哈希表实现

HashMap内部使用哈希表(数组 + 链表/红黑树)来存储键值对。键的哈希码通过哈希函数转换为哈希表的索引。

5.2 哈希函数与冲突解决

元素的哈希码通过哈希函数转换为哈希表的索引。哈希冲突(即不同键的哈希码映射到同一索引)通过链地址法(链表或红黑树)解决。

5.3 扩容机制

HashMap的默认加载因子为0.75,当映射中的元素数量超过当前容量与加载因子的乘积时,哈希表会进行扩容(通常扩容为原容量的2倍),以减少冲突。

5.4 性能特点
  • 插入、删除、查找:平均时间复杂度为O(1)。
  • 遍历:时间复杂度为O(n),元素无序。
6. HashMap 的使用示例
6.1 基本使用
public class HashMapExample {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("Hello", 1);
        map.put("World", 2);
        map.put("Hello", 3);  // 替换 "Hello" 的值为 3

        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
        }

        if (map.containsKey("World")) {
            map.remove("World");
        }

        System.out.println("Map size: " + map.size());
    }
}
6.2 批量操作
public class HashMapBatchOperations {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("Hello", 1);
        map.put("World", 2);

        Map<String, Integer> newEntries = new HashMap<>();
        newEntries.put("Python", 5);
        newEntries.put("JavaScript", 6);
        map.putAll(newEntries);

        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
        }
    }
}
6.3 计算操作
public class HashMapComputeOperations {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("Hello", 1);
        map.put("World", 2);

        // 计算并更新 "Hello" 的值
        map.compute("Hello", (key, value) -> (value == null) ? 1 : value + 1);
        System.out.println("Hello: " + map.get("Hello"));  // 2

        // 仅当 "Ruby" 不存在时添加
        map.computeIfAbsent("Ruby", key -> 7);
        System.out.println("Ruby: " + map.get("Ruby"));  // 7

        // 仅当 "World" 存在时更新
        map.computeIfPresent("World", (key, value) -> value + 1);
        System.out.println("World: " + map.get("World"));  // 3
    }
}
7. HashMap 的最佳实践
  • 使用合适的初始容量:在已知元素数量时,指定合适的初始容量以减少扩容次数。
  • 实现hashCodeequals方法:确保自定义对象的hashCodeequals方法正确实现,以保证HashMap的正确性。
  • 线程安全:在多线程环境中使用同步映射或并发映射(如ConcurrentHashMap)。
  • 避免高负载因子:高负载因子会增加哈希冲突的概率,影响性能。
  • 根据需求选择数据结构HashMap适用于需要快速查找、不关心顺序且允许null键和值的场景。

结论

HashMap是Java中一个常用且强大的集合类,它提供了基于哈希表的高效映射实现,适用于各种场景。通过掌握HashMap的创建、基本操作、高级操作以及最佳实践,能够写出高效且易维护的代码。

(五)TreeMap

Java TreeMap 详解

TreeMap是Java集合框架中的一个重要类,它基于红黑树实现,是一种有序的键值对映射。TreeMap提供了按键排序、范围查找和高效的插入、删除、查找操作。下面将详细介绍TreeMap的使用、内部原理及最佳实践。

1. TreeMap 基本概念
定义

TreeMap是Java集合框架中的类,位于java.util包中,它实现了NavigableMap接口,并继承了AbstractMap类。TreeMap基于红黑树实现,是一个按键排序的有序映射。

主要特性
  • 键值对存储:每个元素存储为一个键值对。
  • 有序:按键的自然顺序或通过提供的比较器排序。
  • 不允许nullTreeMap不允许null键,但允许null值。
  • 基于红黑树:提供高效的插入、删除和查找操作。
  • 线程不安全TreeMap不是线程安全的,适用于单线程环境。
2. TreeMap 的创建与初始化
2.1 无参构造函数

默认初始化一个空的TreeMap,使用键的自然顺序进行排序。

TreeMap<String, Integer> map = new TreeMap<>();
2.2 使用比较器

通过指定比较器来初始化一个TreeMap,使用比较器对键进行排序。

TreeMap<String, Integer> map = new TreeMap<>(Comparator.reverseOrder());
2.3 使用已有映射

通过已有映射初始化一个TreeMap

Map<String, Integer> existingMap = new HashMap<>();
existingMap.put("a", 1);
existingMap.put("b", 2);
TreeMap<String, Integer> map = new TreeMap<>(existingMap);
3. TreeMap 的基本操作
3.1 添加和替换元素
  • put:向映射中添加键值对,如果键已存在,则替换其对应的值。
map.put("Hello", 1);
map.put("World", 2);
map.put("Hello", 3);  // 替换 "Hello" 的值为 3
  • putIfAbsent:仅当键不存在时,才向映射中添加键值对。
map.putIfAbsent("Java", 4);
3.2 获取元素
  • get:通过键获取对应的值,如果键不存在则返回null
Integer value = map.get("Hello");  // 3
  • getOrDefault:通过键获取对应的值,如果键不存在则返回指定的默认值。
Integer value = map.getOrDefault("Python", 0);  // 0
3.3 检查元素
  • containsKey:检查映射中是否包含指定的键。
boolean containsHello = map.containsKey("Hello");  // true
  • containsValue:检查映射中是否包含指定的值。
boolean containsValue = map.containsValue(3);  // true
3.4 删除元素
  • remove:通过键删除对应的键值对。
map.remove("World");
  • remove:通过键和值删除对应的键值对,仅当键和值同时匹配时才删除。
map.remove("Hello", 3);  // 仅当 "Hello" 对应的值为 3 时删除
3.5 大小和清空
  • size:获取映射的大小。
int size = map.size();
  • isEmpty:检查映射是否为空。
boolean isEmpty = map.isEmpty();  // false
  • clear:清空映射。
map.clear();
3.6 遍历映射
  • 遍历键值对
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
  • 遍历键
for (String key : map.keySet()) {
    System.out.println("Key: " + key);
}
  • 遍历值
for (Integer value : map.values()) {
    System.out.println("Value: " + value);
}
4. TreeMap 的高级操作
4.1 获取子映射
  • subMap:获取从fromKey(包括)到toKey(不包括)的子映射。
SortedMap<String, Integer> subMap = map.subMap("a", "c");
  • headMap:获取到toKey(不包括)的子映射。
SortedMap<String, Integer> headMap = map.headMap("c");
  • tailMap:获取从fromKey(包括)的子映射。
SortedMap<String, Integer> tailMap = map.tailMap("b");
4.2 获取边界元素
  • firstKey:获取映射中的第一个(最小)键。
String firstKey = map.firstKey();
  • lastKey:获取映射中的最后一个(最大)键。
String lastKey = map.lastKey();
  • higherKey:获取映射中大于给定键的最小键。
String higherKey = map.higherKey("b");  // c
  • lowerKey:获取映射中小于给定键的最大键。
String lowerKey = map.lowerKey("b");  // a
  • ceilingKey:获取映射中大于或等于给定键的最小键。
String ceilingKey = map.ceilingKey("b");  // b
  • floorKey:获取映射中小于或等于给定键的最大键。
String floorKey = map.floorKey("b");  // b
4.3 批量操作
  • putAll:将一个映射中的所有键值对添加到TreeMap中。
Map<String, Integer> newEntries = new HashMap<>();
newEntries.put("Python", 5);
newEntries.put("JavaScript", 6);
map.putAll(newEntries);
4.4 计算操作
  • compute:对指定的键进行重新计算并更新其值。
map.compute("Hello", (key, value) -> (value == null) ? 1 : value + 1);
  • computeIfAbsent:仅当键不存在时进行计算并更新其值。
map.computeIfAbsent("Ruby", key -> 7);
  • computeIfPresent:仅当键存在时进行计算并更新其值。
map.computeIfPresent("World", (key, value) -> value + 1);
4.5 合并操作
  • merge:如果键不存在,则添加键值对;如果键存在,则对值进行合并。
map.merge("Java", 1, Integer::sum);
4.6 替换操作
  • replace:替换指定键的值,仅当键存在时才替换。
map.replace("Python", 8);
  • replace:仅当键和值同时匹配时才替换。
map.replace("JavaScript", 6, 9);
5. 内部原理与性能
5.1 红黑树实现

TreeMap内部使用红黑树(Red-Black Tree)来存储键值对。红黑树是一种自平衡二叉搜索树,确保插入、删除和查找操作的时间复杂度为O(log n)。

5.2 性能特点
  • 插入、删除、查找:时间复杂度为O(log n)。
  • 遍历:时间复杂度为O(n),元素按键排序顺序遍历。
6. TreeMap 的使用示例
6.1 基本使用
public class TreeMapExample {
    public static void main(String[] args) {
        TreeMap<String, Integer> map = new TreeMap<>();
        map.put("Hello", 1);
        map.put("World", 2);
        map.put("Hello", 3);  // 替换 "Hello" 的值为 3

        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
        }

        if (map.containsKey("World")) {
            map.remove("World");
        }

        System.out.println("Map size: " + map.size());
    }
}
6.2 获取子映射和边界元素
public class TreeMapSubMapExample {
    public static void main(String[] args) {
        TreeMap<String, Integer> map = new TreeMap<>(Arrays.asList("a", "b", "c", "d", "e"));

        // 获取子映射
        SortedMap<String, Integer> subMap = map.subMap("b", "d");
        System.out.println("SubMap: " + subMap);

        // 获取边界元素
        String firstKey = map.firstKey();
        String lastKey = map.lastKey();
        String higherKey = map.higherKey("b");
        String lowerKey = map.lowerKey("b");

        System.out.println("First Key: " + firstKey);
        System.out.println("Last Key: " + lastKey);
        System.out.println("Higher than 'b': " + higherKey);
        System.out.println("Lower than 'b': " + lowerKey);
    }
}
6.3 使用比较器
public class TreeMapComparatorExample {
    public static void main(String[] args) {
        TreeMap<String, Integer> map = new TreeMap<>(Comparator.reverseOrder());
        map.put("Hello", 1);
        map.put("World", 2);
        map.put("Java", 3);

        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
        }
    }
}
7. TreeMap 的最佳实践
  • 使用合适的比较器:根据需求提供自定义比较器,实现自定义排序。
  • 避免使用nullTreeMap不允许null键,避免引发NullPointerException
  • 线程安全:在多线程环境中使用同步映射或并发映射(如ConcurrentSkipListMap)。
  • 根据需求选择数据结构TreeMap适用于需要排序、范围查找且不允许null键的场景。

结论

TreeMap是Java中一个常用且强大的集合类,它提供了基于红黑树的有序映射实现,适用于各种场景。通过掌握TreeMap的创建、基本操作、高级操作以及最佳实践,能够写出高效且易维护的代码。

(六)LinkedHashMaps

LinkedHashMap 是 Java 集合框架中的一个非常有用的类,它结合了 HashMap 的高效性和 LinkedList 的有序性。它不仅继承了 HashMap 的所有特性,还维护了一个双向链表,记录元素的插入顺序或访问顺序。接下来,将详细讲解 LinkedHashMap 及其所有的方法。

1. 基本概念

LinkedHashMapHashMap 的子类,它不仅继承了 HashMap 的所有特性,还维护了一个双向链表,在插入元素时保留其插入顺序或访问顺序。这使得 LinkedHashMap 可以在迭代时按照插入顺序或最近访问顺序返回元素。

2. 构造方法

LinkedHashMap 提供了以下几种构造方法:

  • LinkedHashMap():创建一个初始容量为 16、加载因子为 0.75 的空 LinkedHashMap
  • LinkedHashMap(int initialCapacity):创建一个具有指定初始容量和默认加载因子的空 LinkedHashMap
  • LinkedHashMap(int initialCapacity, float loadFactor):创建一个具有指定初始容量和加载因子的空 LinkedHashMap
  • LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder):创建一个具有指定初始容量、加载因子和顺序模式(插入顺序或访问顺序)的空 LinkedHashMap
  • LinkedHashMap(Map<? extends K, ? extends V> m):创建一个包含指定映射中的所有映射关系的 LinkedHashMap,这些映射关系将按照集合的迭代器返回的顺序排列。

3. 常见方法

LinkedHashMap 继承了 HashMapAbstractMap 的方法,并新增了一些方法。以下是详细介绍:

添加和修改元素
  • V put(K key, V value):将指定值与此映射中的指定键相关联。如果映射之前包含一个该键的映射关系,则替换旧值。
  • void putAll(Map<? extends K, ? extends V> m):将指定映射的所有映射关系复制到此映射中。
  • V putIfAbsent(K key, V value):如果指定键尚未在映射中关联值,则将其与指定值相关联。
  • V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction):尝试计算指定键及其当前映射值(如果存在)的映射关系,并将其重新映射(可以为 null)。
移除元素
  • V remove(Object key):从此映射中移除指定键的映射关系(如果存在)。
  • boolean remove(Object key, Object value):仅在指定键当前映射到指定值时,移除该键的映射关系。
  • void clear():从此映射中移除所有映射关系。
查找元素
  • V get(Object key):返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null
  • boolean containsKey(Object key):如果此映射包含指定键的映射关系,则返回 true
  • boolean containsValue(Object value):如果此映射将一个或多个键映射到指定值,则返回 true
大小和判断
  • int size():返回此映射中的键值对数量。
  • boolean isEmpty():如果此映射不包含键值对,则返回 true
遍历元素
  • Set<K> keySet():返回此映射中包含的键的 Set 视图。
  • Collection<V> values():返回此映射中包含的值的 Collection 视图。
  • Set<Map.Entry<K,V>> entrySet():返回此映射中包含的映射关系的 Set 视图。
顺序控制
  • boolean accessOrder():返回此映射是否按访问顺序(从最近访问到最远访问)维护键的顺序。

4. 例子

以下是一些使用 LinkedHashMap 的示例代码:

基本使用
import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapExample {
    public static void main(String[] args) {
        LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<>();

        // 添加元素
        linkedHashMap.put(1, "One");
        linkedHashMap.put(2, "Two");
        linkedHashMap.put(3, "Three");

        // 检查元素是否存在
        System.out.println("Contains key 2? " + linkedHashMap.containsKey(2)); // 输出 true
        System.out.println("Contains value 'Two'? " + linkedHashMap.containsValue("Two")); // 输出 true

        // 遍历元素
        System.out.println("LinkedHashMap elements:");
        for (Map.Entry<Integer, String> entry : linkedHashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // 移除元素
        linkedHashMap.remove(2);
        System.out.println("After removing key 2, LinkedHashMap elements:");
        for (Map.Entry<Integer, String> entry : linkedHashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // 清空集合
        linkedHashMap.clear();
        System.out.println("Is LinkedHashMap empty? " + linkedHashMap.isEmpty()); // 输出 true
    }
}
使用指定初始容量和加载因子的构造方法
import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapWithCapacityExample {
    public static void main(String[] args) {
        LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<>(10, 0.75f);

        // 添加元素
        linkedHashMap.put(1, "One");
        linkedHashMap.put(2, "Two");
        linkedHashMap.put(3, "Three");

        // 遍历元素
        System.out.println("LinkedHashMap elements:");
        for (Map.Entry<Integer, String> entry : linkedHashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}
使用访问顺序的构造方法
import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapAccessOrderExample {
    public static void main(String[] args) {
        LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<>(10, 0.75f, true);

        // 添加元素
        linkedHashMap.put(1, "One");
        linkedHashMap.put(2, "Two");
        linkedHashMap.put(3, "Three");

        // 访问元素
        linkedHashMap.get(1);
        linkedHashMap.get(3);

        // 遍历元素
        System.out.println("LinkedHashMap elements (access order):");
        for (Map.Entry<Integer, String> entry : linkedHashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}

5. 内部实现

LinkedHashMap 通过 HashMap 和双向链表实现。这意味着 LinkedHashMap 维护了一个链接列表,该列表将所有插入的元素按插入顺序或访问顺序存储,同时使用哈希表来实现快速的查找。

关键点
  • 哈希表:用于快速查找元素。
  • 双向链表:用于维护元素的插入顺序或访问顺序。
LinkedHashMap 的基本结构
  • 头指针和尾指针:用于维护插入或访问顺序。
  • 哈希表:用于快速查找和插入元素。

6. 使用场景

  • 保留插入顺序:当需要保留元素的插入顺序时,LinkedHashMap 是一个理想选择。
  • 按访问顺序迭代:当需要按最近访问顺序迭代元素时,LinkedHashMap 可以通过设置访问顺序模式实现。
  • 缓存实现:可以基于 LinkedHashMap 实现简单的 LRU(最近最少使用)缓存。

7. 注意事项

  • 性能LinkedHashMap 的查找、插入、删除操作的时间复杂度为 O(1)。
  • 非线程安全LinkedHashMap 不是线程安全的,如果需要在多线程环境中使用,可以使用 Collections.synchronizedMap

(七)HashSet

Java HashSet 详解

HashSet是Java集合框架中的一个重要类,它基于哈希表实现,是一种不允许有重复元素且不保证顺序的集合。HashSet提供了高效的插入、删除和查找操作。下面将详细介绍HashSet的使用、内部原理及最佳实践。

1. HashSet 基本概念
定义

HashSet是Java集合框架中的类,位于java.util包中,它实现了Set接口。HashSet基于哈希表实现,是一个不允许有重复元素的集合。

主要特性
  • 无重复:不允许有重复元素。
  • 无序:不保证元素的插入顺序。
  • 允许null:可以包含一个空值(null)。
  • 基于哈希表:提供高效的插入、删除和查找操作。
  • 线程不安全HashSet不是线程安全的,适用于单线程环境。
2. HashSet 的创建与初始化
2.1 无参构造函数

默认初始化一个空的HashSet

HashSet<String> set = new HashSet<>();
2.2 指定初始容量和加载因子

可以指定初始容量和加载因子来初始化一个HashSet

HashSet<String> set = new HashSet<>(16, 0.75f);
2.3 使用已有集合

通过已有集合初始化一个HashSet

Set<String> existingSet = new HashSet<>(Arrays.asList("a", "b", "c"));
HashSet<String> set = new HashSet<>(existingSet);
3. HashSet 的基本操作
3.1 添加元素
  • add:向集合中添加元素,如果元素已存在则不添加。
set.add("Hello");
set.add("World");
3.2 删除元素
  • remove:从集合中删除指定元素,如果元素不存在则不做任何操作。
set.remove("World");
3.3 检查元素
  • contains:检查集合中是否包含指定元素。
boolean containsHello = set.contains("Hello");  // true
3.4 大小和清空
  • size:获取集合的大小。
int size = set.size();
  • isEmpty:检查集合是否为空。
boolean isEmpty = set.isEmpty();  // false
  • clear:清空集合。
set.clear();
3.5 遍历集合
  • 增强型for循环
for (String s : set) {
    System.out.println(s);
}
  • 使用Iterator
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
4. HashSet 的高级操作
4.1 批量操作
  • addAll:将一个集合中的所有元素添加到HashSet中,忽略重复元素。
Set<String> newElements = new HashSet<>(Arrays.asList("a", "b", "c"));
set.addAll(newElements);
  • removeAll:移除集合中存在于指定集合中的所有元素。
set.removeAll(newElements);
  • retainAll:保留集合中存在于指定集合中的所有元素。
set.retainAll(newElements);
4.2 转换为其他集合
  • 转换为ArrayList
List<String> list = new ArrayList<>(set);
  • 转换为数组
String[] array = set.toArray(new String[0]);
5. 内部原理与性能
5.1 哈希表实现

HashSet内部使用HashMap来存储元素。每个添加到HashSet中的元素实际上是作为HashMap的键来存储的,值则是一个固定的常量。

5.2 哈希函数与冲突

元素的哈希码通过哈希函数转换为哈希表的索引,如果发生哈希冲突(即不同元素的哈希码映射到同一索引),使用链地址法来解决冲突。

5.3 加载因子与扩容

HashSet的默认加载因子是0.75,当集合中的元素数量超过当前容量与加载因子的乘积时,哈希表会进行扩容(通常扩容为原容量的2倍),以减少冲突。

5.4 性能特点
  • 插入、删除、查找:平均时间复杂度为O(1)。
  • 遍历:时间复杂度为O(n)。
6. HashSet 的使用示例
6.1 基本使用
public class HashSetExample {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("Hello");
        set.add("World");
        set.add("Hello");  // 重复元素不会被添加

        for (String s : set) {
            System.out.println(s);
        }

        if (set.contains("World")) {
            set.remove("World");
        }

        System.out.println("Set size: " + set.size());
    }
}
6.2 集合操作
public class HashSetOperations {
    public static void main(String[] args) {
        HashSet<String> set1 = new HashSet<>(Arrays.asList("a", "b", "c"));
        HashSet<String> set2 = new HashSet<>(Arrays.asList("b", "c", "d"));

        // 并集
        HashSet<String> union = new HashSet<>(set1);
        union.addAll(set2);
        System.out.println("Union: " + union);

        // 交集
        HashSet<String> intersection = new HashSet<>(set1);
        intersection.retainAll(set2);
        System.out.println("Intersection: " + intersection);

        // 差集
        HashSet<String> difference = new HashSet<>(set1);
        difference.removeAll(set2);
        System.out.println("Difference: " + difference);
    }
}
6.3 去除重复元素
public class RemoveDuplicates {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c", "a", "b", "c");
        HashSet<String> set = new HashSet<>(list);
        System.out.println("Unique elements: " + set);
    }
}
7. HashSet 的最佳实践
  • 使用合适的初始容量:在已知元素数量时,指定合适的初始容量以减少扩容次数。
  • 实现hashCodeequals方法:确保自定义对象的hashCodeequals方法正确实现,以保证HashSet的正确性。
  • 避免过高的加载因子:加载因子过高会增加哈希冲突的概率,影响性能。
  • 线程安全:在多线程环境中使用同步集合或并发集合。
  • 根据需求选择数据结构HashSet适用于需要快速查找、不允许重复且不关心顺序的场景。

结论

HashSet是Java中一个常用且强大的集合类,它提供了基于哈希表的高效集合实现,适用于各种场景。通过掌握HashSet的创建、基本操作、高级操作以及最佳实践,能够写出高效且易维护的代码。

(八)TreeSet

Java TreeSet 详解

TreeSet是Java集合框架中的一个重要类,它基于红黑树实现,是一个有序且不允许重复元素的集合。TreeSet提供了排序、范围查找和高效的插入、删除、查找操作。下面将详细介绍TreeSet的使用、内部原理及最佳实践。

1. TreeSet 基本概念
定义

TreeSet是Java集合框架中的类,位于java.util包中,它实现了NavigableSet接口,并继承了AbstractSet类。TreeSet基于红黑树实现,是一个有序且不允许重复元素的集合。

主要特性
  • 有序:自然顺序或通过提供的比较器排序。
  • 不允许重复:不允许有重复元素。
  • 基于红黑树:提供高效的插入、删除和查找操作。
  • 允许null:不允许null元素(从Java 7开始)。
  • 线程不安全TreeSet不是线程安全的,适用于单线程环境。
2. TreeSet 的创建与初始化
2.1 无参构造函数

默认初始化一个空的TreeSet,使用元素的自然顺序进行排序。

TreeSet<String> set = new TreeSet<>();
2.2 使用比较器

通过指定比较器来初始化一个TreeSet,使用比较器排序。

TreeSet<String> set = new TreeSet<>(Comparator.reverseOrder());
2.3 使用已有集合

通过已有集合初始化一个TreeSet

Set<String> existingSet = new HashSet<>(Arrays.asList("a", "b", "c"));
TreeSet<String> set = new TreeSet<>(existingSet);
3. TreeSet 的基本操作
3.1 添加元素
  • add:向集合中添加元素,如果元素已存在则不添加。
set.add("Hello");
set.add("World");
3.2 删除元素
  • remove:从集合中删除指定元素,如果元素不存在则不做任何操作。
set.remove("World");
3.3 检查元素
  • contains:检查集合中是否包含指定元素。
boolean containsHello = set.contains("Hello");  // true
3.4 大小和清空
  • size:获取集合的大小。
int size = set.size();
  • isEmpty:检查集合是否为空。
boolean isEmpty = set.isEmpty();  // false
  • clear:清空集合。
set.clear();
3.5 遍历集合
  • 增强型for循环
for (String s : set) {
    System.out.println(s);
}
  • 使用Iterator
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
4. TreeSet 的高级操作
4.1 获取子集
  • subSet:获取从fromElement(包括)到toElement(不包括)的子集。
NavigableSet<String> subset = set.subSet("a", true, "c", false);
  • headSet:获取到toElement(不包括)的子集。
NavigableSet<String> headset = set.headSet("c", false);
  • tailSet:获取从fromElement(包括)的子集。
NavigableSet<String> tailset = set.tailSet("b", true);
4.2 获取边界元素
  • first:获取集合中的第一个(最小)元素。
String first = set.first();
  • last:获取集合中的最后一个(最大)元素。
String last = set.last();
  • higher:获取集合中大于给定元素的最小元素。
String higher = set.higher("b");
  • lower:获取集合中小于给定元素的最大元素。
String lower = set.lower("b");
  • ceiling:获取集合中大于或等于给定元素的最小元素。
String ceiling = set.ceiling("b");
  • floor:获取集合中小于或等于给定元素的最大元素。
String floor = set.floor("b");
4.3 批量操作
  • addAll:将一个集合中的所有元素添加到TreeSet中,忽略重复元素。
Set<String> newElements = new HashSet<>(Arrays.asList("a", "b", "c"));
set.addAll(newElements);
  • removeAll:移除集合中存在于指定集合中的所有元素。
set.removeAll(newElements);
  • retainAll:保留集合中存在于指定集合中的所有元素。
set.retainAll(newElements);
4.4 转换为其他集合
  • 转换为ArrayList
List<String> list = new ArrayList<>(set);
  • 转换为数组
String[] array = set.toArray(new String[0]);
5. 内部原理与性能
5.1 红黑树实现

TreeSet内部使用红黑树(Red-Black Tree)来存储元素。红黑树是一种自平衡二叉搜索树,确保插入、删除和查找操作的时间复杂度为O(log n)。

5.2 性能特点
  • 插入、删除、查找:时间复杂度为O(log n)。
  • 遍历:时间复杂度为O(n),元素按排序顺序遍历。
6. TreeSet 的使用示例
6.1 基本使用
public class TreeSetExample {
    public static void main(String[] args) {
        TreeSet<String> set = new TreeSet<>();
        set.add("Hello");
        set.add("World");
        set.add("Hello");  // 重复元素不会被添加

        for (String s : set) {
            System.out.println(s);
        }

        if (set.contains("World")) {
            set.remove("World");
        }

        System.out.println("Set size: " + set.size());
    }
}
6.2 获取子集和边界元素
public class TreeSetSubSetExample {
    public static void main(String[] args) {
        TreeSet<String> set = new TreeSet<>(Arrays.asList("a", "b", "c", "d", "e"));

        // 获取子集
        NavigableSet<String> subset = set.subSet("b", true, "d", true);
        System.out.println("Subset: " + subset);

        // 获取边界元素
        String first = set.first();
        String last = set.last();
        String higher = set.higher("b");
        String lower = set.lower("b");

        System.out.println("First: " + first);
        System.out.println("Last: " + last);
        System.out.println("Higher than 'b': " + higher);
        System.out.println("Lower than 'b': " + lower);
    }
}
6.3 使用比较器
public class TreeSetComparatorExample {
    public static void main(String[] args) {
        TreeSet<String> set = new TreeSet<>(Comparator.reverseOrder());
        set.add("Hello");
        set.add("World");
        set.add("Java");

        for (String s : set) {
            System.out.println(s);
        }
    }
}
7. TreeSet 的最佳实践
  • 使用合适的比较器:根据需求提供自定义比较器,实现自定义排序。
  • 避免使用null元素TreeSet不允许null元素,避免引发NullPointerException
  • 线程安全:在多线程环境中使用同步集合或并发集合。
  • 根据需求选择数据结构TreeSet适用于需要排序、范围查找且不允许重复的场景。

结论

TreeSet是Java中一个常用且强大的集合类,它提供了基于红黑树的有序集合实现,适用于各种场景。通过掌握TreeSet的创建、基本操作、高级操作以及最佳实践,能够写出高效且易维护的代码。

(九)LinkedHashSet

LinkedHashSet 是 Java 集合框架中的一个非常有用的类,它结合了 HashSet 的高效性和 LinkedList 的有序性。

1. 基本概念

LinkedHashSetHashSet 的子类,它不仅继承了 HashSet 的所有特性,还维护了一个双向链表,在插入元素时保留其插入顺序。这使得 LinkedHashSet 可以在迭代时按照插入顺序返回元素。

2. 构造方法

LinkedHashSet 提供了以下几种构造方法:

  • LinkedHashSet():创建一个初始容量为 16、加载因子为 0.75 的空 LinkedHashSet
  • LinkedHashSet(int initialCapacity):创建一个具有指定初始容量和默认加载因子的空 LinkedHashSet
  • LinkedHashSet(int initialCapacity, float loadFactor):创建一个具有指定初始容量和加载因子的空 LinkedHashSet
  • LinkedHashSet(Collection<? extends E> c):创建一个包含指定集合元素的 LinkedHashSet,这些元素将按照集合的迭代器返回的顺序排列。

3. 常见方法

LinkedHashSet 继承了 HashSetAbstractSet 的方法,并没有添加新的方法,它的所有方法都来自于 HashSet。以下是详细介绍:

添加元素
  • boolean add(E e):将指定元素添加到 LinkedHashSet 中,如果元素已经存在则返回 false
  • boolean addAll(Collection<? extends E> c):将指定集合中的所有元素添加到 LinkedHashSet
移除元素
  • boolean remove(Object o):移除 LinkedHashSet 中指定的元素,如果存在则返回 true
  • void clear():移除 LinkedHashSet 中的所有元素。
  • boolean removeAll(Collection<?> c):移除 LinkedHashSet 中那些在指定集合中也存在的元素。
  • boolean retainAll(Collection<?> c):仅保留 LinkedHashSet 中那些在指定集合中也存在的元素。
查找元素
  • boolean contains(Object o):如果 LinkedHashSet 中包含指定元素,则返回 true
  • boolean containsAll(Collection<?> c):如果 LinkedHashSet 包含指定集合中的所有元素,则返回 true
大小和判断
  • int size():返回 LinkedHashSet 中的元素数量。
  • boolean isEmpty():如果 LinkedHashSet 为空,则返回 true
遍历元素
  • Iterator<E> iterator():返回 LinkedHashSet 中元素的迭代器。
  • Spliterator<E> spliterator():返回支持分割操作的 Spliterator
其他
  • Object[] toArray():返回包含 LinkedHashSet 中所有元素的数组。
  • <T> T[] toArray(T[] a):返回包含 LinkedHashSet 中所有元素的数组,数组类型与参数一致。

4. 例子

以下是一些使用 LinkedHashSet 的示例代码:

基本使用
import java.util.LinkedHashSet;
import java.util.Iterator;

public class LinkedHashSetExample {
    public static void main(String[] args) {
        LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();

        // 添加元素
        linkedHashSet.add(1);
        linkedHashSet.add(2);
        linkedHashSet.add(3);
        linkedHashSet.add(1); // 重复元素不会被添加

        // 检查元素是否存在
        System.out.println("Contains 2? " + linkedHashSet.contains(2)); // 输出 true

        // 遍历元素
        System.out.println("LinkedHashSet elements:");
        for (Integer element : linkedHashSet) {
            System.out.println(element); // 输出顺序为 1, 2, 3
        }

        // 移除元素
        linkedHashSet.remove(2);
        System.out.println("After removing 2, LinkedHashSet elements:");
        for (Integer element : linkedHashSet) {
            System.out.println(element); // 输出顺序为 1, 3
        }

        // 清空集合
        linkedHashSet.clear();
        System.out.println("Is LinkedHashSet empty? " + linkedHashSet.isEmpty()); // 输出 true
    }
}
使用集合初始化
import java.util.LinkedHashSet;
import java.util.Arrays;

public class LinkedHashSetFromCollectionExample {
    public static void main(String[] args) {
        // 使用集合初始化 LinkedHashSet
        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>(Arrays.asList("A", "B", "C", "A"));

        // 遍历元素
        System.out.println("LinkedHashSet elements:");
        for (String element : linkedHashSet) {
            System.out.println(element); // 输出顺序为 A, B, C
        }
    }
}
去除重复元素,并保持顺序

假设你有一个包含重复元素的列表,你想要去除重复元素并保持元素的顺序:

import java.util.LinkedHashSet;
import java.util.List;
import java.util.ArrayList;

public class RemoveDuplicates {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("One");
        list.add("Two");
        list.add("Three");
        list.add("Two");
        list.add("One");
        
        LinkedHashSet<String> set = new LinkedHashSet<>(list);
        System.out.println(set); // 输出: [One, Two, Three]
        
        // 如果需要返回一个 List
        List<String> uniqueList = new ArrayList<>(set);
        System.out.println(uniqueList); // 输出: [One, Two, Three]
    }
}

5. 内部实现

LinkedHashSet 通过 LinkedHashMap 来实现。这意味着 LinkedHashSet 维护了一个链接列表,该列表将所有插入的元素按插入顺序存储,同时使用哈希表来实现快速的查找。

关键点
  • 哈希表:用于快速查找元素。
  • 双向链表:用于维护元素的插入顺序。
LinkedHashSet 的基本结构
  • 头指针和尾指针:用于维护插入顺序。
  • 哈希表:用于快速查找和插入元素。

6. 使用场景

  • 保留插入顺序:当需要保留元素的插入顺序时,LinkedHashSet 是一个理想选择。
  • 避免重复元素:用于需要快速查找且不允许重复元素的场景。
  • 迭代有序集合:适用于需要以插入顺序迭代集合的应用。

7. 注意事项

  • 性能LinkedHashSet 的查找、插入、删除操作的时间复杂度为 O(1)。
  • 非线程安全LinkedHashSet 不是线程安全的,如果需要在多线程环境中使用,可以使用 Collections.synchronizedSet

(十)PriorityQueue

PriorityQueue 是 Java 提供的一种特殊队列,它能够保证每次访问的元素都是队列中优先级最高的元素。

1. 基本概念

PriorityQueue 是 Java 集合框架中的一个类,它实现了 Queue 接口。PriorityQueue 基于优先级堆(通常是二叉堆)实现,能够确保每次从队列中取出的元素都是队列中优先级最高的元素。

2. 特性

  • 优先级排序PriorityQueue 中的元素会按照自然顺序(或者通过提供的比较器)进行排序。
  • 线程不安全PriorityQueue 不是线程安全的;如果需要在多线程环境中使用,可以使用 PriorityBlockingQueue
  • 无界队列PriorityQueue 是一个无界队列,但它的内部容量会根据需要自动增加。

3. 构造方法

PriorityQueue 提供了多种构造方法,常用的有以下几种:

  • PriorityQueue():创建一个默认初始容量(11)的空优先级队列,元素按照自然顺序排序。
  • PriorityQueue(int initialCapacity):创建一个具有指定初始容量的空优先级队列,元素按照自然顺序排序。
  • PriorityQueue(int initialCapacity, Comparator<? super E> comparator):创建一个具有指定初始容量的空优先级队列,并使用指定的比较器对元素进行排序。
  • PriorityQueue(Collection<? extends E> c):创建一个包含指定集合元素的优先级队列,元素按照自然顺序排序。
  • PriorityQueue(PriorityQueue<? extends E> c):创建一个包含指定优先级队列元素的新优先级队列,元素按照自然顺序排序。
  • PriorityQueue(SortedSet<? extends E> c):创建一个包含指定有序集合元素的优先级队列,元素按照自然顺序排序。

4. 常见方法

PriorityQueue 提供了许多方法,以下是一些常用方法的详细介绍:

添加元素
  • boolean add(E e):将指定元素插入到优先级队列中。成功返回 true,失败抛出异常。
  • boolean offer(E e):将指定元素插入到优先级队列中。成功返回 true,失败返回 false
移除元素
  • E poll():获取并移除优先级队列的头部,如果队列为空,则返回 null
  • E remove():获取并移除优先级队列的头部,如果队列为空,则抛出 NoSuchElementException
  • boolean remove(Object o):从优先级队列中移除指定元素,成功返回 true,失败返回 false
检索元素
  • E peek():获取但不移除优先级队列的头部,如果队列为空,则返回 null
  • E element():获取但不移除优先级队列的头部,如果队列为空,则抛出 NoSuchElementException
大小和判断
  • int size():返回优先级队列中的元素数量。
  • boolean isEmpty():如果优先级队列为空,则返回 true,否则返回 false
其他
  • void clear():移除优先级队列中的所有元素。
  • boolean contains(Object o):如果优先级队列中包含指定元素,则返回 true,否则返回 false
  • Object[] toArray():返回包含优先级队列中所有元素的数组。
  • <T> T[] toArray(T[] a):返回包含优先级队列中所有元素的数组,数组类型与参数一致。
  • Iterator<E> iterator():返回优先级队列中元素的迭代器。

5. 例子

以下是一些使用 PriorityQueue 的例子:

基本使用
import java.util.PriorityQueue;

public class PriorityQueueExample {
    public static void main(String[] args) {
        // 创建一个优先级队列
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        
        // 添加元素
        pq.add(10);
        pq.add(20);
        pq.add(15);
        
        // 检索头部元素
        System.out.println("Head of the queue: " + pq.peek()); // 输出 10
        
        // 遍历优先级队列
        System.out.println("PriorityQueue elements:");
        for (Integer value : pq) {
            System.out.println(value);
        }
        
        // 移除并获取头部元素
        System.out.println("Poll: " + pq.poll()); // 输出 10
        System.out.println("Poll: " + pq.poll()); // 输出 15
        System.out.println("Poll: " + pq.poll()); // 输出 20
    }
}
使用比较器(Comparator)
import java.util.PriorityQueue;
import java.util.Comparator;

public class CustomPriorityQueueExample {
    public static void main(String[] args) {
        // 创建一个优先级队列,并使用自定义比较器
        PriorityQueue<Integer> pq = new PriorityQueue<>(Comparator.reverseOrder());
        
        pq.add(10);
        pq.add(20);
        pq.add(15);
        
        System.out.println("PriorityQueue elements:");
        while (!pq.isEmpty()) {
            System.out.println(pq.poll()); // 输出 20, 15, 10
        }
    }
}

6. 内部实现

PriorityQueue 的内部实现基于数组的二叉堆。默认的初始容量是 11,当容量不足时,队列的容量会自动扩展到原来的 1.5 倍。

主要字段
  • Object[] queue:存储元素的数组。
  • int size:当前队列中的元素数量。
  • Comparator<? super E> comparator:用于元素比较的比较器,如果为 null,则使用元素的自然顺序。
主要操作
  • 插入:将新元素插入到数组末尾,然后上浮(heapify-up)以维护堆的性质。
  • 删除:将堆顶元素移除,用数组末尾元素填补堆顶位置,然后下沉(heapify-down)以维护堆的性质。

7. 使用场景

  • 任务调度PriorityQueue 可以用于实现基于优先级的任务调度。
  • 事件处理:在实时系统中,PriorityQueue 常用于事件驱动的模拟和处理。
  • 路径搜索算法:如 Dijkstra 和 A* 算法,PriorityQueue 经常用于实现这些算法中的优先级队列。

(十一)ArrayDeque

ArrayDeque 是 Java 集合框架中的一个非常灵活且高效的双端队列(Deque)。它是双端队列的一个实现,能够在两端进行插入和删除操作。接下来,将尽可能详细地讲解 ArrayDeque 及其所有的方法。

1. 基本概念

ArrayDeque 是一个基于动态数组实现的双端队列,支持在队列两端进行高效的插入和删除操作。它是非线程安全的,如果需要在多线程环境中使用,可以使用 Collections.synchronizedDeque

2. 构造方法

ArrayDeque 提供了以下几种构造方法:

  • ArrayDeque():创建一个初始容量为 16 的空双端队列。
  • ArrayDeque(int numElements):创建一个具有指定初始容量的空双端队列。
  • ArrayDeque(Collection<? extends E> c):创建一个包含指定集合中的元素的双端队列,这些元素将按照集合的迭代器返回的顺序排列。

3. 常见方法

ArrayDeque 提供了许多方法,以下是详细介绍:

添加元素
  • void addFirst(E e):将指定元素插入到双端队列的开头。
  • void addLast(E e):将指定元素插入到双端队列的末尾。
  • boolean offerFirst(E e):将指定元素插入到双端队列的开头,成功返回 true,失败返回 false
  • boolean offerLast(E e):将指定元素插入到双端队列的末尾,成功返回 true,失败返回 false
  • boolean add(E e):将指定元素插入到双端队列的末尾,等同于 addLast
  • boolean offer(E e):将指定元素插入到双端队列的末尾,等同于 offerLast
  • void push(E e):将元素推入双端队列的栈顶,等同于 addFirst
移除元素
  • E removeFirst():移除并返回双端队列的第一个元素,如果队列为空则抛出 NoSuchElementException
  • E removeLast():移除并返回双端队列的最后一个元素,如果队列为空则抛出 NoSuchElementException
  • E pollFirst():移除并返回双端队列的第一个元素,如果队列为空则返回 null
  • E pollLast():移除并返回双端队列的最后一个元素,如果队列为空则返回 null
  • boolean removeFirstOccurrence(Object o):移除双端队列中首次出现的指定元素,成功返回 true,失败返回 false
  • boolean removeLastOccurrence(Object o):移除双端队列中最后一次出现的指定元素,成功返回 true,失败返回 false
  • E remove():移除并返回双端队列的第一个元素,等同于 removeFirst
  • E poll():移除并返回双端队列的第一个元素,等同于 pollFirst
  • E pop():移除并返回双端队列的栈顶元素,等同于 removeFirst
检索元素
  • E getFirst():返回双端队列的第一个元素,不移除它,如果队列为空则抛出 NoSuchElementException
  • E getLast():返回双端队列的最后一个元素,不移除它,如果队列为空则抛出 NoSuchElementException
  • E peekFirst():返回双端队列的第一个元素,不移除它,如果队列为空则返回 null
  • E peekLast():返回双端队列的最后一个元素,不移除它,如果队列为空则返回 null
  • E element():返回双端队列的第一个元素,等同于 getFirst
  • E peek():返回双端队列的第一个元素,等同于 peekFirst
大小和判断
  • int size():返回双端队列中的元素数量。
  • boolean isEmpty():如果双端队列为空,则返回 true,否则返回 false
其他
  • void clear():移除双端队列中的所有元素。
  • boolean contains(Object o):如果双端队列中包含指定元素,则返回 true,否则返回 false
  • Object[] toArray():返回包含双端队列中所有元素的数组。
  • <T> T[] toArray(T[] a):返回包含双端队列中所有元素的数组,数组类型与参数一致。
  • Iterator<E> iterator():返回双端队列中元素的迭代器。
  • Iterator<E> descendingIterator():返回双端队列中元素的逆向迭代器。

4. 例子

以下是一些使用 ArrayDeque 的例子:

基本使用
import java.util.ArrayDeque;

public class ArrayDequeExample {
    public static void main(String[] args) {
        ArrayDeque<Integer> deque = new ArrayDeque<>();

        // 添加元素
        deque.addFirst(1);
        deque.addLast(2);
        deque.add(3); // 等同于 addLast(3)

        // 检索元素
        System.out.println("First Element: " + deque.getFirst()); // 输出 1
        System.out.println("Last Element: " + deque.getLast()); // 输出 3

        // 遍历双端队列
        System.out.println("Deque elements:");
        for (Integer element : deque) {
            System.out.println(element);
        }

        // 移除元素
        System.out.println("Removed First: " + deque.removeFirst()); // 输出 1
        System.out.println("Removed Last: " + deque.removeLast()); // 输出 3

        // 检索但不移除元素
        System.out.println("First Element: " + deque.peekFirst()); // 输出 2
    }
}
作为栈使用
import java.util.ArrayDeque;

public class StackExample {
    public static void main(String[] args) {
        ArrayDeque<String> stack = new ArrayDeque<>();

        // 压栈
        stack.push("A");
        stack.push("B");
        stack.push("C");

        // 弹栈
        System.out.println("Popped: " + stack.pop()); // 输出 C
        System.out.println("Peek: " + stack.peek()); // 输出 B

        // 遍历栈
        System.out.println("Stack elements:");
        for (String element : stack) {
            System.out.println(element);
        }
    }
}
作为队列使用
import java.util.ArrayDeque;

public class QueueExample {
    public static void main(String[] args) {
        ArrayDeque<String> queue = new ArrayDeque<>();

        // 入队
        queue.offer("X");
        queue.offer("Y");
        queue.offer("Z");

        // 出队
        System.out.println("Polled: " + queue.poll()); // 输出 X
        System.out.println("Peek: " + queue.peek()); // 输出 Y

        // 遍历队列
        System.out.println("Queue elements:");
        for (String element : queue) {
            System.out.println(element);
        }
    }
}

5. 内部实现

ArrayDeque 是基于循环数组实现的,这使得它能够高效地在数组两端进行插入和删除操作。以下是一些关键点:

动态数组

ArrayDeque 内部维护一个动态扩展的数组,当容量不够时,它会自动扩展。

索引管理
  • 头部索引(head):指向队列头部的索引。
  • 尾部索引(tail):指向队列尾部的索引。
操作实现
  • 插入:在头部插入时,head 向前移动;在尾部插入时,tail 向后移动。
  • 删除:在头部删除时,head 向后移动;在尾部删除时,tail 向前移动。

6. 使用场景

  • 双端队列ArrayDeque 可以作为双端队列使用,支持在两端进行高效的插入和删除操作。
  • ArrayDeque 可以作为栈使用,支持后进先出(LIFO)操作。
  • 队列ArrayDeque 可以作为队列使用,支持先进先出(FIFO)操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值