目录
概述
Java提供了丰富的数据结构来处理各种编程任务,这些数据结构包括基本数据类型、集合框架和自定义数据结构。以下是一些常见的Java数据结构:
-
基本数据类型: Java的基本数据类型包括整数(int、byte、short、long)、浮点数(float、double)、字符(char)、布尔(boolean)等。
-
数组(Array): 数组是一组相同类型的元素的集合,它具有固定的大小。在Java中,数组的大小在创建时确定,无法更改。
-
集合框架(Collection Framework): Java提供了一组用于存储和操作对象的集合框架。这些集合框架包括:
- List接口: 有序集合,可以包含重复元素。常见的实现类包括ArrayList和LinkedList。
- Set接口: 不允许重复元素的集合。常见的实现类包括HashSet、LinkedHashSet和TreeSet。
- Map接口: 键值对的集合,每个键映射到唯一的值。常见的实现类包括HashMap、LinkedHashMap和TreeMap。
- Queue接口: 队列,支持在队列的一端插入元素,在另一端移除元素。常见的实现类包括LinkedList和PriorityQueue。
-
堆栈(Stack): 堆栈是一种特殊的数据结构,遵循后进先出(LIFO)原则。可以使用Java的Stack类实现堆栈。
-
链表(LinkedList): 链表是一种线性数据结构,包含节点,每个节点指向下一个节点。Java的LinkedList类实现了双向链表。
-
树(Tree): 树是一种层次结构的数据结构,包括根节点、子节点和叶节点。Java提供了TreeSet和TreeMap来实现树结构。
-
图(Graph): 图是一种复杂的数据结构,由节点和边组成,用于表示实体之间的关系。在Java中,可以使用自定义数据结构来表示图,也可以使用图算法库。
-
堆(Heap): 堆是一种特殊的树结构,通常用于实现优先队列。Java的PriorityQueue类使用堆来维护元素的顺序。
-
哈希表(HashTable): 哈希表是一种使用哈希函数将键映射到值的数据结构。Java的HashMap和Hashtable类都是哈希表的实现。
-
自定义数据结构: 根据具体需求,您还可以创建自定义的数据结构,如栈、队列、链表、树等。
这些数据结构在Java中提供了广泛的应用,可以根据问题的性质选择合适的数据结构来实现算法和数据处理任务。 Java的集合框架尤其强大,提供了各种数据结构的实现,可以满足不同场景的需求。
介绍
1、Array
在Java中,数组(Array)是一种用于存储相同数据类型的元素的数据结构。数组具有固定的大小,一旦创建,其大小不能更改。每个数组元素都可以通过索引访问,索引从0开始,依次递增。
示例
- 声明和创建数组:
dataType[] arrayName; // 声明数组
arrayName = new dataType[arraySize]; // 创建数组
// 或者
int[] numbers; // 声明整数数组
numbers = new int[5]; // 创建包含5个整数元素的数组
// 或者
int[] numbers = new int[5]; // 声明并创建整数数组
- 初始化数组:
int[] numbers = {1, 2, 3, 4, 5}; // 初始化整数数组
String[] names = {"Alice", "Bob", "Charlie"}; // 初始化字符串数组
-
访问数组元素: 使用索引来访问数组元素。索引从0开始,依次递增。
int firstNumber = numbers[0]; // 获取第一个元素(索引为0)
String secondName = names[1]; // 获取第二个元素(索引为1)
-
数组长度: 使用
length
属性来获取数组的长度。
int length = numbers.length; // 获取整数数组的长度(包含5个元素)
- 多维数组: Java支持多维数组,例如二维数组(数组的数组)。
int[][] matrix = {
{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; // 二维整数数组
int element = matrix[1][2]; // 访问二维数组中的元素
- 数组遍历: 可以使用循环来遍历数组中的元素。
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]);
}
或者使用增强型for
循环(for-each循环):
for (int number : numbers) {
System.out.println(number);
}
数组是Java中常见的数据结构,用于存储和操作一组数据。请根据具体需求和场景选择使用数组。如果需要更灵活的数据结构,可以考虑使用集合类(例如ArrayList、LinkedList等)来替代数组。
2、ArrayList
ArrayList
是 Java 中的一个常用集合类,它属于 Java 集合框架的一部分,位于 java.util
包中。ArrayList
提供了动态数组的实现,允许存储和管理一组对象。与普通数组不同,ArrayList
的大小可以动态调整,可以自动扩展以容纳更多元素。
示例
- 声明和创建 ArrayList:
import java.util.ArrayList;
// 声明一个整数类型的 ArrayList
ArrayList<Integer> numbers = new ArrayList<Integer>();
// 声明一个字符串类型的 ArrayList
ArrayList<String> names = new ArrayList<String>();
-
添加元素:
可以使用 add
方法将元素添加到 ArrayList
中:
numbers.add(10);
numbers.add(20);
names.add("Alice");
names.add("Bob");
-
获取元素:
使用 get
方法根据索引获取 ArrayList
中的元素:
int firstNumber = numbers.get(0); // 获取第一个元素
String secondName = names.get(1); // 获取第二个元素
-
修改元素:
使用 set
方法根据索引修改 ArrayList
中的元素:
numbers.set(0, 30); // 将第一个元素修改为30
names.set(1, "Charlie"); // 将第二个元素修改为"Charlie"
-
删除元素:
使用 remove
方法根据索引或对象删除 ArrayList
中的元素:
numbers.remove(0); // 删除第一个元素
names.remove("Alice"); // 删除指定元素
-
获取大小:
使用 size
方法获取 ArrayList
的大小(元素数量):
int size = numbers.size(); // 获取整数 ArrayList 的大小
-
遍历元素:
可以使用循环遍历 ArrayList
中的元素:
for (int i = 0; i < numbers.size(); i++) {
int number = numbers.get(i);
System.out.println(number);
}
for (String name : names) {
System.out.println(name);
}
-
判空:
可以使用 isEmpty
方法检查 ArrayList
是否为空:
boolean isEmpty = numbers.isEmpty(); // 检查整数 ArrayList 是否为空
ArrayList
是一个灵活且常用的数据结构,适用于需要动态管理元素集合的情况。需要注意的是,由于 ArrayList
是基于数组实现的,插入和删除元素的开销较大,如果需要频繁执行插入和删除操作,可能需要考虑其他集合类,如 LinkedList
。
3、LinkedList
LinkedList
是 Java 中的一个双向链表数据结构,它实现了 List
和 Deque
接口,是 Java 集合框架的一部分,位于 java.util
包中。与 ArrayList
不同,LinkedList
不是基于数组实现的,而是通过节点之间的链接来组织元素。
链表结构:
LinkedList
使用链表结构来存储元素。每个元素(节点)都包含一个数据项和两个指针,分别指向前一个节点和后一个节点,这使得在链表中插入和删除元素的操作相对容易。双向链表:
LinkedList
是双向链表,意味着可以从任何一个节点开始,向前或向后遍历链表,这有助于在链表中执行向前和向后的操作。随机访问较慢: 与
ArrayList
不同,LinkedList
不支持快速的随机访问。要访问链表中的元素,通常需要从头节点(或尾节点)开始,顺着链表一个一个地遍历,因此随机访问的时间复杂度为 O(n)。插入和删除操作: 由于链表的结构,插入和删除元素的操作在
LinkedList
中相对高效。在链表中插入或删除一个节点的时间复杂度是 O(1)。
示例
import java.util.LinkedList;
// 创建一个 LinkedList
LinkedList<String> linkedList = new LinkedList<>();
// 向链表添加元素
linkedList.add("Alice");
linkedList.add("Bob");
linkedList.add("Charlie");
// 在指定位置插入元素
linkedList.add(1, "David");
// 访问元素
String first = linkedList.getFirst();
String last = linkedList.getLast();
// 删除元素
linkedList.remove(2); // 删除索引为2的元素
// 遍历链表
for (String name : linkedList) {
System.out.println(name);
}
// 获取链表的大小
int size = linkedList.size();
总之,LinkedList
是一个灵活的数据结构,特别适合需要频繁插入和删除操作的场景。然而,如果需要大量的随机访问元素,ArrayList
可能更合适,因为 LinkedList
的随机访问性能较差。根据具体的应用场景,选择合适的数据结构是很重要的。
4、HashSet
HashSet
是 Java 中的一种集合类,它实现了 Set
接口,是集合框架的一部分,位于 java.util
包中。HashSet
主要用于存储一组不重复的元素,它不保证元素的顺序,也不允许重复元素。
不重复元素:
HashSet
存储的元素是不重复的,如果试图向HashSet
中添加已经存在的元素,添加操作会被忽略,不会导致元素重复。无序集合:
HashSet
不保证元素的顺序。元素在HashSet
中的存储顺序可能与元素被添加的顺序不同。基于哈希表:
HashSet
内部使用哈希表来存储元素。这使得添加、删除和查找元素的操作通常具有常数时间复杂度,即 O(1)。支持 null 元素:
HashSet
允许存储一个null
元素,但只能存储一个,因为集合中不允许重复元素。
示例
import java.util.HashSet;
import java.util.Set;
// 创建一个 HashSet
Set<String> hashSet = new HashSet<>();
// 向 HashSet 添加元素
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Cherry");
// 判断元素是否存在
boolean containsBanana = hashSet.contains("Banana"); // 返回 true
// 删除元素
hashSet.remove("Cherry");
// 遍历 HashSet
for (String fruit : hashSet) {
System.out.println(fruit);
}
// 获取 HashSet 的大小
int size = hashSet.size();
总之,HashSet
是一种常用的集合类,适用于需要存储一组不重复元素的情况。由于其高效的添加、删除和查找操作,它通常是首选的集合类型。如果需要保持元素的顺序或允许重复元素,可以考虑使用其他集合类型,如 LinkedHashSet
或 TreeSet
。
5、LinkedHashSet
LinkedHashSet
是 Java 中的一种集合类,它是 HashSet
的一个变种,实现了 Set
接口,位于 java.util
包中。与普通的 HashSet
不同,LinkedHashSet
在内部使用链表来维护元素的顺序,因此它保留了元素的插入顺序。
-
有序集合:
LinkedHashSet
保留了元素的插入顺序,因此它是一个有序集合。当你遍历LinkedHashSet
时,元素的顺序与它们被添加到集合中的顺序相同。 -
不重复元素:
LinkedHashSet
存储的元素是不重复的,不允许重复元素。 -
基于哈希表和链表:
LinkedHashSet
内部使用哈希表来快速查找元素,并使用链表来维护元素的顺序。这使得添加、删除和查找元素的操作通常具有常数时间复杂度,即 O(1)。 -
支持 null 元素:
LinkedHashSet
允许存储一个null
元素,但只能存储一个,因为集合中不允许重复元素。
示例
import java.util.LinkedHashSet;
import java.util.Set;
// 创建一个 LinkedHashSet
Set<String> linkedHashSet = new LinkedHashSet<>();
// 向 LinkedHashSet 添加元素
linkedHashSet.add("Apple");
linkedHashSet.add("Banana");
linkedHashSet.add("Cherry");
// 保留元素的插入顺序
// 遍历 LinkedHashSet 时,元素的顺序与添加顺序相同
for (String fruit : linkedHashSet) {
System.out.println(fruit);
}
// 判断元素是否存在
boolean containsBanana = linkedHashSet.contains("Banana"); // 返回 true
// 删除元素
linkedHashSet.remove("Cherry");
// 获取 LinkedHashSet 的大小
int size = linkedHashSet.size();
总之,LinkedHashSet
是一种有序集合,它保留了元素的插入顺序。如果你需要在集合中保持元素的顺序,并且不允许重复元素,LinkedHashSet
是一个很好的选择。它的性能特性与普通的 HashSet
相似,但有序性质使其适用于特定的应用场景。
6、TreeSet
TreeSet
是 Java 中的一种集合类,它实现了 NavigableSet
接口,是 SortedSet
接口的一个实现,位于 java.util
包中。TreeSet
是一个有序的集合,它使用红黑树(Red-Black Tree)数据结构来维护元素的顺序,具有以下特点:
- 有序集合:
TreeSet
是有序集合,它根据元素的自然顺序或指定的比较器对元素进行排序。默认情况下,TreeSet
使用元素的自然顺序来排序。- 不重复元素:
TreeSet
存储的元素是不重复的,不允许重复元素。- 基于红黑树:
TreeSet
内部使用红黑树数据结构来存储元素。这种数据结构保证了元素的快速查找、插入和删除,时间复杂度为 O(log n)。- 自然排序或自定义排序: 可以根据元素的自然顺序(如果元素实现了
Comparable
接口)或者通过提供自定义的比较器(Comparator
)来对元素进行排序。- 遍历顺序: 当你遍历
TreeSet
时,元素将按照排序顺序进行迭代。- 不支持 null 元素:
TreeSet
不允许存储null
元素。
示例
import java.util.TreeSet;
import java.util.Set;
// 创建一个 TreeSet
Set<String> treeSet = new TreeSet<>();
// 向 TreeSet 添加元素
treeSet.add("Banana");
treeSet.add("Apple");
treeSet.add("Cherry");
// 遍历 TreeSet,按照排序顺序输出
for (String fruit : treeSet) {
System.out.println(fruit);
}
// 判断元素是否存在
boolean containsBanana = treeSet.contains("Banana"); // 返回 true
// 删除元素
treeSet.remove("Cherry");
// 获取 TreeSet 的大小
int size = treeSet.size();
总之,TreeSet
是一种有序集合,适用于需要对元素进行排序的情况。它具有高效的插入、删除和查找操作,但在插入和删除操作时会进行平衡红黑树,因此插入和删除操作的性能略低于 HashSet
。根据排序要求和性能需求,选择合适的集合类型非常重要。
7、HashMap
HashMap
是 Java 中的一种集合类,它实现了 Map
接口,用于存储键值对。HashMap
以键作为索引,可以快速查找和获取与键相关联的值。以下是关于 HashMap
的一些重要特点和用法:
键值对存储:
HashMap
存储的数据是键值对(Key-Value Pair)的集合。每个键都唯一,每个键关联一个值。不重复键:
HashMap
中的键是不重复的,如果试图添加已经存在的键,新的值会覆盖原有的值。基于哈希表:
HashMap
内部使用哈希表数据结构来实现,这使得查找、插入和删除操作通常具有常数时间复杂度,即 O(1)。但在某些情况下,哈希冲突可能会导致性能下降。无序集合:
HashMap
不保证元素的顺序,即元素的存储顺序可能与添加顺序不同。如果需要有序的键值对,可以考虑使用LinkedHashMap
。允许 null 键和值:
HashMap
允许存储一个null
键和多个null
值。线程不安全:
HashMap
不是线程安全的,如果多个线程同时访问和修改同一个HashMap
实例,可能会导致数据不一致和并发问题。可以使用Collections.synchronizedMap()
方法或ConcurrentHashMap
来获得线程安全的版本。
示例
import java.util.HashMap;
import java.util.Map;
// 创建一个 HashMap
Map<String, Integer> hashMap = new HashMap<>();
// 向 HashMap 添加键值对
hashMap.put("Alice", 25);
hashMap.put("Bob", 30);
hashMap.put("Charlie", 28);
// 获取键对应的值
int age = hashMap.get("Bob"); // 返回 30
// 判断是否包含键
boolean containsAlice = hashMap.containsKey("Alice"); // 返回 true
// 删除键值对
hashMap.remove("Charlie");
// 遍历 HashMap
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
String name = entry.getKey();
int value = entry.getValue();
System.out.println(name + ": " + value);
}
// 获取 HashMap 的大小
int size = hashMap.size();
8、LinkedHashMap
LinkedHashMap
是 Java 中的一种集合类,它是 HashMap
的一个变种,实现了 Map
接口,位于 java.util
包中。与普通的 HashMap
不同,LinkedHashMap
在内部使用哈希表和双向链表来维护元素的顺序,因此具有以下特点:
-
有序集合:
LinkedHashMap
保留了元素的插入顺序,因此它是有序集合。当你遍历LinkedHashMap
时,元素的顺序与它们被添加到集合中的顺序相同。 -
不重复键:
LinkedHashMap
中的键是不重复的,不允许重复键。 -
基于哈希表和双向链表:
LinkedHashMap
内部使用哈希表来快速查找元素,并使用双向链表来维护元素的插入顺序。这种数据结构使得LinkedHashMap
具有快速的查找和遍历元素的能力。 -
可以指定排序方式:
LinkedHashMap
允许你根据插入顺序或访问顺序对元素进行排序。你可以在构造LinkedHashMap
时传入参数来选择排序方式。 -
不支持 null 键和值:
LinkedHashMap
不允许存储null
键和值。
示例
import java.util.LinkedHashMap;
import java.util.Map;
// 创建一个 LinkedHashMap
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
// 向 LinkedHashMap 添加键值对
linkedHashMap.put("Alice", 25);
linkedHashMap.put("Bob", 30);
linkedHashMap.put("Charlie", 28);
// 保留元素的插入顺序
// 遍历 LinkedHashMap 时,元素的顺序与添加顺序相同
for (Map.Entry<String, Integer> entry : linkedHashMap.entrySet()) {
String name = entry.getKey();
int value = entry.getValue();
System.out.println(name + ": " + value);
}
// 判断是否包含键
boolean containsBob = linkedHashMap.containsKey("Bob"); // 返回 true
// 删除键值对
linkedHashMap.remove("Charlie");
// 获取 LinkedHashMap 的大小
int size = linkedHashMap.size();
总之,LinkedHashMap
是一种有序集合,适用于需要保持元素插入顺序的情况。它具有快速的查找和遍历元素的能力,是有序集合的一种常见选择。如果需要根据元素的排序规则进行排序,可以使用 TreeMap
。
9、TreeMap
TreeMap
是 Java 中的一种集合类,它实现了 NavigableMap
接口,是 SortedMap
接口的一个实现,位于 java.util
包中。TreeMap
是一种基于红黑树数据结构的有序映射,具有以下特点:
-
有序映射:
TreeMap
是有序映射,它根据键的自然顺序或指定的比较器对键进行排序。默认情况下,TreeMap
使用键的自然顺序来排序。 -
不重复键:
TreeMap
中的键是不重复的,不允许重复键。如果试图添加已经存在的键,新的值会覆盖原有的值。 -
基于红黑树:
TreeMap
内部使用红黑树(Red-Black Tree)数据结构来存储键值对。这种数据结构保证了键的快速查找、插入和删除,时间复杂度为 O(log n)。 -
自然排序或自定义排序: 可以根据键的自然顺序(如果键实现了
Comparable
接口)或者通过提供自定义的比较器(Comparator
)来对键进行排序。 -
遍历顺序: 当你遍历
TreeMap
时,键值对将按照排序顺序进行迭代。 -
不支持 null 键:
TreeMap
不允许存储null
键,但可以存储null
值。
示例
import java.util.TreeMap;
import java.util.Map;
// 创建一个 TreeMap
Map<String, Integer> treeMap = new TreeMap<>();
// 向 TreeMap 添加键值对
treeMap.put("Alice", 25);
treeMap.put("Bob", 30);
treeMap.put("Charlie", 28);
// 遍历 TreeMap,按照排序顺序输出
for (Map.Entry<String, Integer> entry : treeMap.entrySet()) {
String name = entry.getKey();
int value = entry.getValue();
System.out.println(name + ": " + value);
}
// 获取键对应的值
int age = treeMap.get("Bob"); // 返回 30
// 判断是否包含键
boolean containsAlice = treeMap.containsKey("Alice"); // 返回 true
// 删除键值对
treeMap.remove("Charlie");
// 获取 TreeMap 的大小
int size = treeMap.size();
总之,TreeMap
是一种有序映射,适用于需要根据键的排序规则来存储和访问数据的情况。它具有高效的插入、删除和查找操作,但在插入和删除操作时会进行平衡红黑树,因此插入和删除操作的性能略低于 HashMap
。根据排序要求和性能需求,选择合适的集合类型非常重要。
10、HashTable
Hashtable
是 Java 中的一种集合类,它实现了 Map
接口,位于 java.util
包中。Hashtable
是一种基于哈希表数据结构的集合,具有以下特点:
-
键值对存储:
Hashtable
存储的数据是键值对(Key-Value Pair)的集合。每个键都唯一,每个键关联一个值。 -
不重复键:
Hashtable
中的键是不重复的,如果试图添加已经存在的键,新的值会覆盖原有的值。 -
基于哈希表:
Hashtable
内部使用哈希表数据结构来实现,这使得查找、插入和删除操作通常具有常数时间复杂度,即 O(1)。 -
线程安全:
Hashtable
是线程安全的,多个线程可以同时访问和修改同一个Hashtable
实例而不会导致数据不一致。但这也意味着在多线程环境下,Hashtable
的性能会相对较低。如果不需要线程安全性,可以考虑使用HashMap
。 -
不支持 null 键和值:
Hashtable
不允许存储null
键和值。 -
遍历无序:
Hashtable
不保证元素的顺序,即元素的存储顺序可能与添加顺序不同。
示例
import java.util.Hashtable;
import java.util.Map;
// 创建一个 Hashtable
Map<String, Integer> hashtable = new Hashtable<>();
// 向 Hashtable 添加键值对
hashtable.put("Alice", 25);
hashtable.put("Bob", 30);
hashtable.put("Charlie", 28);
// 获取键对应的值
int age = hashtable.get("Bob"); // 返回 30
// 判断是否包含键
boolean containsAlice = hashtable.containsKey("Alice"); // 返回 true
// 删除键值对
hashtable.remove("Charlie");
// 遍历 Hashtable
for (Map.Entry<String, Integer> entry : hashtable.entrySet()) {
String name = entry.getKey();
int value = entry.getValue();
System.out.println(name + ": " + value);
}
// 获取 Hashtable 的大小
int size = hashtable.size();
总之,Hashtable
是一种线程安全的哈希表集合,适用于需要在多线程环境下使用的情况。它具有快速的查找、插入和删除操作,但在多线程环境下的性能相对较低。如果不需要线程安全性,可以考虑使用 HashMap
。
11、ConcurrentHashMap
ConcurrentHashMap
是Java集合框架中的一种线程安全的哈希表实现,它是在Java 5中引入的,用于解决多线程环境下对HashMap的并发访问问题。ConcurrentHashMap
支持高并发的读和写操作,而不需要显式地加锁,因此在多线程环境下表现出色。
线程安全性:
ConcurrentHashMap
是线程安全的,多个线程可以同时读取和写入数据,而不会导致数据不一致或并发问题。分段锁:
ConcurrentHashMap
使用了分段锁的概念,内部将数据分为多个段(Segment),每个段独立地加锁。这意味着不同段的操作可以并发执行,提高了并发性能。高并发读取: 在
ConcurrentHashMap
中,多个线程可以同时读取数据,而不会阻塞。这使得在读多写少的场景下性能非常高。适用于高并发写入: 虽然读取操作可以高度并发,但写入操作也可以在不同的段上并发执行,因此
ConcurrentHashMap
在高并发写入的情况下仍然表现出色。无锁读取: 在读取操作中,
ConcurrentHashMap
通常不需要加锁,因此对于读取操作来说是无锁的。允许空键值: 与HashMap不同,
ConcurrentHashMap
允许键和值都为null。性能优化: Java 8及以后版本对
ConcurrentHashMap
进行了性能优化,进一步提高了并发性能。
示例用法
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// 添加元素
concurrentMap.put("key1", 1);
concurrentMap.put("key2", 2);
// 获取元素
int value = concurrentMap.get("key1");
// 删除元素
concurrentMap.remove("key2");
// 遍历元素
concurrentMap.forEach((key, val) -> {
System.out.println(key + ": " + val);
});
总之,ConcurrentHashMap
是在多线程环境下处理并发访问哈希表的强大工具,适用于高并发读取和写入的场景,能够提供良好的性能和线程安全性。
12、Stack
Stack
是 Java 中的一种集合类,它实现了栈(Stack)数据结构。栈是一种后进先出(Last-In-First-Out,LIFO)的数据结构,通常用于在顶部添加或删除元素。Stack
类继承自 Vector
类,位于 java.util
包中。
-
后进先出: 栈是一种后进先出的数据结构,最后添加的元素会被最先移除。
-
基于数组:
Stack
内部使用数组来存储元素,因此具有动态扩展的能力。 -
主要操作:
push(E item)
:将元素压入栈顶。pop()
:移除并返回栈顶元素。peek()
:返回栈顶元素,但不移除。empty()
:检查栈是否为空。search(Object o)
:查找元素在栈中的位置,返回距离栈顶的距离。
示例
import java.util.Stack;
// 创建一个 Stack
Stack<Integer> stack = new Stack<>();
// 压入元素
stack.push(1);
stack.push(2);
stack.push(3);
// 弹出元素
int popped = stack.pop(); // 弹出并返回 3
// 查看栈顶元素
int top = stack.peek(); // 返回 2,但不移除
// 检查栈是否为空
boolean isEmpty = stack.isEmpty(); // 返回 false
// 查找元素在栈中的位置
int index = stack.search(1); // 返回 2,表示距离栈顶的第 2 个元素是 1
总之,Stack
是一种后进先出的数据结构,通常用于临时存储和管理数据,例如在算法、递归和表达式求值中。在实际开发中,由于 Stack
是 Vector
的子类,通常建议使用 Deque
(双端队列)接口的实现类 LinkedList
作为栈的替代方案,因为 LinkedList
既支持栈操作,又支持队列操作,并且性能更好。
13、PriorityQueue
PriorityQueue
是 Java 中的一种集合类,它实现了队列(Queue)接口,是一种特殊的队列,用于存储具有优先级的元素。元素被存储在队列中,根据它们的优先级决定出队的顺序。PriorityQueue
是一个最小堆(Min-Heap)的实现,它的顶部元素是具有最高优先级的元素。位于 java.util
包中。
-
优先级队列:
PriorityQueue
是一种优先级队列,其中的元素根据它们的优先级进行排序。默认情况下,元素被认为是按自然顺序进行排序,或者你可以提供一个自定义的比较器来指定排序方式。 -
插入和删除操作:
PriorityQueue
支持插入(offer()
或add()
方法)和删除(poll()
方法)操作。插入操作将元素添加到队列中,删除操作移除并返回队列中的最高优先级元素。 -
不允许 null 元素:
PriorityQueue
不允许存储 null 元素。 -
基于堆:
PriorityQueue
内部使用二叉堆(binary heap)数据结构实现,它是一种完全二叉树,保证了插入和删除的效率为 O(log n),其中 n 是队列的大小。 -
自定义排序: 你可以通过提供一个自定义的比较器(
Comparator
)来指定元素的排序方式。这允许你在元素之间定义任何类型的优先级关系。
示例
import java.util.PriorityQueue;
import java.util.Queue;
// 创建一个 PriorityQueue,默认按自然顺序排序
Queue<Integer> priorityQueue = new PriorityQueue<>();
// 插入元素
priorityQueue.offer(5);
priorityQueue.offer(3);
priorityQueue.offer(8);
// 删除并返回最高优先级元素
int highestPriority = priorityQueue.poll(); // 返回 3
// 创建一个自定义排序的 PriorityQueue
Queue<String> customPriorityQueue = new PriorityQueue<>((a, b) -> b.length() - a.length());
// 插入元素,根据长度倒序排序
customPriorityQueue.offer("apple");
customPriorityQueue.offer("banana");
customPriorityQueue.offer("cherry");
// 删除并返回最高优先级元素
String highestLength = customPriorityQueue.poll(); // 返回 "banana"
总之,PriorityQueue
是一种用于实现优先级队列的有序集合。它在很多应用中非常有用,如任务调度、负载均衡等,可以根据元素的优先级进行排序和处理。可以通过提供自定义的比较器来实现灵活的排序方式。
14、Tree
在 Java 中,"Tree" 通常用来描述树形数据结构或树形集合,而不是特指一种名为 "Tree" 的类。树(Tree)是一种重要的数据结构,它以分层结构的方式组织和存储数据,常用于实现搜索、排序、层次结构等。
-
TreeNode
接口: 通常,树的节点会实现TreeNode
接口或其子接口MutableTreeNode
。这些接口提供了节点在树中的基本操作,如获取父节点、获取子节点、添加子节点等。 -
TreeModel
接口:TreeModel
接口用于表示树形数据模型,通常与图形用户界面(GUI)的树状控件(如JTree
)结合使用。它定义了树形结构中的数据组织和访问方式。 -
DefaultMutableTreeNode
类: 这是一个常用的树节点实现类,通常用于创建可变树(Mutable Tree)。 -
JTree
类:JTree
是 Java Swing 中的一个图形用户界面组件,用于显示和交互树形数据结构。它通常与TreeModel
和节点类一起使用。 -
树的遍历算法: Java 中可以使用递归或迭代方式实现树的遍历,包括前序遍历、中序遍历、后序遍历和层序遍历等。这些遍历算法用于访问树的节点。
-
二叉树类: Java 中可以自定义二叉树的节点和二叉树数据结构,用于实现二叉树操作,如二叉搜索树(BST)等。
示例
使用 DefaultMutableTreeNode
和 JTree
创建一个简单的可变树
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
public class SimpleTreeExample {
public static void main(String[] args) {
// 创建根节点
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
// 创建子节点
DefaultMutableTreeNode node1 = new DefaultMutableTreeNode("Node 1");
DefaultMutableTreeNode node2 = new DefaultMutableTreeNode("Node 2");
// 添加子节点到根节点
root.add(node1);
root.add(node2);
// 创建树模型
DefaultTreeModel treeModel = new DefaultTreeModel(root);
// 创建树控件
JTree tree = new JTree(treeModel);
// 创建窗口并添加树控件
JFrame frame = new JFrame("Simple Tree Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(tree));
frame.pack();
frame.setVisible(true);
}
}
这个示例创建了一个简单的可变树,其中包含根节点和两个子节点,然后通过 JTree
显示出来。
总之,Java 中的树可以通过多种方式和类来实现,用于表示和处理具有层次结构的数据。在图形用户界面开发中,JTree
是常用的树形控件,用于显示和操作树形数据。在算法和数据结构中,树结构也广泛应用于搜索、排序、分层结构等问题的解决。
15、Graph
在 Java 中,表示图(Graph)这种数据结构通常需要自己实现,因为 Java 标准库中并没有提供专门的 Graph
类。图是一种非常重要的数据结构,用于表示多个对象之间的关系,它包括节点(顶点)和边(连接)。以下是一种常见的方法来表示和操作图:
-
节点类(Vertex): 通常,你需要创建一个节点类来表示图中的每个节点。节点类通常包括一个标识节点的唯一标识符,以及与节点相关的其他信息。
-
边类(Edge): 边用于连接两个节点,通常也包括一些额外的信息,例如权重(Weight)。
-
图类(Graph): 图类用于表示整个图,它包括一个节点集合和一个边集合。你可以选择使用邻接矩阵、邻接链表或其他数据结构来实现图。
示例
以下是一个简单的示例,展示了如何用 Java 来表示一个有向图(Directed Graph):
import java.util.ArrayList;
import java.util.List;
class Vertex {
private int id;
private List<Edge> edges;
public Vertex(int id) {
this.id = id;
this.edges = new ArrayList<>();
}
public int getId() {
return id;
}
public List<Edge> getEdges() {
return edges;
}
public void addEdge(Vertex destination, int weight) {
edges.add(new Edge(this, destination, weight));
}
}
class Edge {
private Vertex source;
private Vertex destination;
private int weight;
public Edge(Vertex source, Vertex destination, int weight) {
this.source = source;
this.destination = destination;
this.weight = weight;
}
public Vertex getSource() {
return source;
}
public Vertex getDestination() {
return destination;
}
public int getWeight() {
return weight;
}
}
public class DirectedGraph {
private List<Vertex> vertices;
public DirectedGraph() {
this.vertices = new ArrayList<>();
}
public void addVertex(Vertex vertex) {
vertices.add(vertex);
}
public List<Vertex> getVertices() {
return vertices;
}
}
在这个示例中,我们定义了 Vertex
类来表示图中的节点,Edge
类来表示边,以及 DirectedGraph
类来表示有向图。你可以创建节点、添加边,然后构建一个有向图。
需要注意的是,上述示例是一个简单的演示,实际应用中可能需要更复杂的数据结构和算法来表示和操作图。此外,还有一些 Java 第三方库,如 JGraphT 和 JUNG,提供了更高级的图处理功能,可以根据具体需求选择使用。
16、Heap
在 Java 中,"Heap" 通常指的是堆内存,而不是数据结构中的堆(Heap)。堆内存是 Java 虚拟机(JVM)中用于存储对象的一部分内存,与栈内存相对。以下是有关 Java 堆内存的一些重要信息:
-
Java 堆内存: 堆内存是 Java 程序中用于存储对象实例的内存区域。它是 Java 内存管理的核心部分,用于动态分配和回收对象内存。堆内存的大小可以通过 JVM 的启动参数进行配置。
-
对象分配: 当你创建一个新的对象时,它通常会在堆内存中分配内存空间。堆内存中的对象生命周期不受方法的限制,而是由对象的引用和垃圾回收机制来管理。
-
垃圾回收: 堆内存中的对象不会永远存在,Java 垃圾回收机制会定期扫描堆内存,标记和回收不再被引用的对象,释放它们占用的内存。这有助于防止内存泄漏和管理内存。
-
堆内存区域: 堆内存通常被分为不同的区域,例如新生代(Young Generation)、老年代(Old Generation)和持久代(PermGen)。新生代主要用于存放新创建的对象,而老年代主要用于存放生存时间较长的对象。持久代(在 Java 8 及之前版本中,Java 8 后被元空间(Metaspace)替代)用于存放类信息和方法等。
-
内存管理: Java 提供了自动内存管理,开发者无需手动分配或释放内存。Java 垃圾回收器会负责处理内存的分配和回收,确保内存的正确使用。
-
内存泄漏: 尽管 Java 有自动内存管理,但仍然可能发生内存泄漏,即对象被持续引用而无法被回收。开发者需要注意及时释放不再使用的对象引用,以避免内存泄漏问题。
示例
以下是一个简单的示例,展示了如何在 Java 中创建对象并将其分配到堆内存中:
public class HeapExample {
public static void main(String[] args) {
// 创建对象并分配到堆内存
String str = new String("Hello, World!");
// 在不再使用对象时,不要忘记将引用设为 null,以便垃圾回收
str = null;
}
}
在这个示例中,我们创建了一个字符串对象 str
,并将其分配到堆内存中。当我们不再需要这个对象时,将引用设为 null
可以帮助垃圾回收器识别并回收不再被引用的对象。这是一种防止内存泄漏的做法。