几种储存数据的方式
-
数组(Array):数组是一种最基本的数据结构,用于存储固定大小的相同类型元素的连续内存空间。数组具有固定长度,一旦创建后长度不能更改。
声明数组:
// 声明一个整数数组 int[] intArray;// 声明一个字符串数组 String[] stringArray;// 声明一个对象数组 Object[] objectArray;// 声明一个二维整数数组 int[][] twoDimensionalIntArray;// 声明一个三维整数数组 int[][][] threeDimensionalIntArray;
在声明数组后,需要使用
new
关键字来分配数组的内存空间,并指定数组的大小。例如:// 声明并初始化一个整数数组,包含5个元素 int[] intArray = new int[5];// 声明并初始化一个字符串数组,包含3个元素 String[] stringArray = new String[3];// 声明并初始化一个对象数组,包含4个元素 Object[] objectArray = new Object[4];// 声明并初始化一个二维整数数组,包含3行4列 int[][] twoDimensionalIntArray = new int[3][4];// 声明并初始化一个三维整数数组,包含2个2×3的二维数组 int[][][] threeDimensionalIntArray = new int[2][2][3];
-
集合(Collection):集合是 Java 中用于存储和操作一组对象的容器。Java 提供了一系列集合类,包括 List、Set、Queue 和 Map 等。常见的集合类有 ArrayList、LinkedList、HashSet、TreeSet、HashMap 等。
声明集合:
ArrayList:
import java.util.ArrayList;// 声明一个存储字符串的 ArrayList ArrayList<String> stringList = new ArrayList<>();
LinkedList:
import java.util.LinkedList;// 声明一个存储整数的 LinkedList LinkedList<Integer> integerList = new LinkedList<>();
HashSet:
import java.util.HashSet;// 声明一个存储浮点数的 HashSet HashSet<Double> doubleSet = new HashSet<>();
TreeSet:
import java.util.TreeSet;// 声明一个存储字符的 TreeSet TreeSet<Character> charSet = new TreeSet<>();
HashMap:
import java.util.HashMap;// 声明一个键为字符串、值为整数的 HashMap HashMap<String, Integer> stringIntegerMap = new HashMap<>();
TreeMap:
import java.util.TreeMap;// 声明一个键为整数、值为字符串的 TreeMap TreeMap<Integer, String> integerStringMap = new TreeMap<>();
在声明集合后,可以使用
add
方法向集合中添加元素,使用remove
方法删除元素,使用get
方法获取元素等。 -
列表(List):列表是一种有序的集合,允许重复元素。Java 中的 List 接口有多种实现,如 ArrayList、LinkedList、Vector 等,其中 ArrayList 是最常用的。
// 声明一个ArrayList列表 List<String> list = new ArrayList<>(); // 声明一个LinkedList列表 List<String> list = new LinkedList<>();
相同点:
- 实现了 List 接口:Vector、ArrayList 和 LinkedList 都实现了 List 接口,因此它们都是有序集合,可以按索引访问元素。
- 允许存储重复元素:这三种集合类都允许存储重复元素。
- 支持动态增长:当集合中的元素超出其初始容量时,它们都能够动态增长以容纳更多元素。
不同点:
- 线程安全性:
- Vector 是线程安全的,它的方法都是同步的(synchronized),因此适合在多线程环境中使用。
- ArrayList 和 LinkedList 不是线程安全的,因此在多线程环境中使用时需要额外的同步措施。
- 底层数据结构:
- ArrayList 底层是基于数组实现的,它通过数组来存储元素,因此支持快速的随机访问和遍历,但插入和删除操作可能会比较耗时,特别是在数组的中间位置。
- LinkedList 底层是基于链表实现的,它通过链表来存储元素,因此插入和删除操作比较快,特别是在链表的中间位置,但随机访问和遍历可能会比较耗时。
- 性能差异:
- 对于随机访问和修改元素,ArrayList 的性能比 LinkedList 更好,因为 ArrayList 支持通过索引直接访问元素,时间复杂度为 O(1);而 LinkedList 需要遍历链表来查找元素,时间复杂度为 O(n)。
- 对于插入和删除操作,尤其是在集合的中间位置,LinkedList 的性能比 ArrayList 更好,因为 LinkedList 只需要修改节点的指针,时间复杂度为 O(1),而 ArrayList 需要移动元素,时间复杂度为 O(n)。
-
集(Set):集是一种不允许重复元素的集合,主要实现类有 HashSet、TreeSet 等。
Set<String> set1 = new HashSet<>(); // 使用 HashSet Set<Integer> set2 = new TreeSet<>(); // 使用 TreeSet Set<Double> set3 = new LinkedHashSet<>(); // 使用 LinkedHashSet
相同点:
- 实现了 Set 接口:HashSet、TreeSet 和 LinkedHashSet 都实现了 Set 接口,因此它们都不允许包含重复元素,保持了集合元素的唯一性。
- 不保证元素顺序:HashSet 和 TreeSet 不保证元素的插入顺序,而 LinkedHashSet 则保留了元素插入的顺序。
- 不允许存储 null 元素:这三种集合类都不允许存储 null 元素,如果试图将 null 元素添加到集合中,将会抛出 NullPointerException 异常。
不同点:
- 底层数据结构:
- HashSet 使用哈希表(HashMap)来存储元素,因此它的元素是无序的,但是插入、删除和查找操作的时间复杂度都是 O(1)。
- TreeSet 使用红黑树(TreeMap)来存储元素,因此它能够保持元素的排序状态,插入、删除和查找操作的时间复杂度都是 O(log n),并且元素是有序的。
- LinkedHashSet 使用哈希表和链表来存储元素,它保留了元素插入的顺序,并且插入、删除和查找操作的时间复杂度都是 O(1)。
- 性能和排序:
- HashSet 具有最好的性能,适合在大多数情况下使用,但不保证元素的顺序。
- TreeSet 保持元素的排序状态,可以根据自然顺序或者指定的 Comparator 进行排序,但是由于使用了红黑树,性能相对于 HashSet 会有一些损耗。
- LinkedHashSet 在 HashSet 的基础上保留了元素插入的顺序,并且性能接近于 HashSet。
- 迭代顺序:
- HashSet 的迭代顺序是不确定的,因为它是基于哈希表实现的。
- TreeSet 的迭代顺序是有序的,根据元素的自然顺序或者指定的 Comparator 进行排序。
- LinkedHashSet 的迭代顺序是元素插入的顺序,因为它使用链表来维护插入顺序。
综上所述,选择适合的集合类取决于具体的需求:如果需要快速的插入、删除和查找操作,并且不关心元素的顺序,则可以选择 HashSet;如果需要保持元素的排序状态,并且可以接受性能损耗,则可以选择 TreeSet;如果需要保持元素插入的顺序,并且具有较好的性能,则可以选择 LinkedHashSet。
-
映射(Map):映射是一种键值对的集合,每个键都映射到一个值。Java 中的 Map 接口有多种实现,如 HashMap、TreeMap、LinkedHashMap 等。
声明:
-
使用 HashMap 类声明:
Map<KeyType, ValueType> map = new HashMap<>();
这种方式是最常见的声明 Map 的方式,使用 HashMap 类作为实现,在大多数情况下都可以满足需求。
-
使用 TreeMap 类声明:
Map<KeyType, ValueType> map = new TreeMap<>();
TreeMap 是基于红黑树的实现,它可以保持元素的排序状态,按照键的自然顺序或者指定的 Comparator 进行排序。
-
使用 LinkedHashMap 类声明:
Map<KeyType, ValueType> map = new LinkedHashMap<>();
LinkedHashMap 继承自 HashMap,它保留了元素插入的顺序,在迭代时可以按照元素插入的顺序访问元素。
相同点:
- 实现了 Map 接口:HashMap、TreeMap 和 LinkedHashMap 都实现了 Map 接口,因此它们都用于存储键值对,并提供了对键值对进行增删改查操作的方法。
- 允许存储 null 键和 null 值:这三种 Map 都允许存储 null 键和 null 值,但需要注意的是,HashMap 中只能有一个 null 键,而 TreeMap 和 LinkedHashMap 中则可以有一个 null 键和多个 null 值。
不同点:
- 底层数据结构不同:
- HashMap 使用哈希表(数组 + 链表/红黑树)来存储键值对,具有 O(1) 的常数时间复杂度的增删改查操作(在均匀散列的情况下)。
- TreeMap 基于红黑树实现,可以保持键值对的有序状态,根据键的自然顺序或指定的比较器进行排序,因此它的键值对是有序的,但增删改查操作的时间复杂度是 O(log n)。
- LinkedHashMap 继承自 HashMap,使用哈希表和双向链表来存储键值对,它保留了元素插入的顺序,因此可以按照插入顺序迭代元素,但性能接近于 HashMap。
- 性能和排序:
- HashMap 具有最好的性能,适合大多数情况下的使用,但不保证元素的顺序。
- TreeMap 可以保持元素的有序状态,但由于使用了红黑树,性能相对于 HashMap 会有一些损耗。
- LinkedHashMap 在 HashMap 的基础上保留了元素插入的顺序,并且性能接近于 HashMap。
- 迭代顺序:
- HashMap 的迭代顺序是不确定的,因为它是基于哈希表实现的。
- TreeMap 的迭代顺序是根据键的自然顺序或指定的比较器进行排序的。
- LinkedHashMap 的迭代顺序是元素插入的顺序,因为它使用链表来维护插入顺序。
综上所述,选择适合的 Map 实现类取决于具体的需求:如果需要快速的增删改查操作,并且不关心元素的顺序,则可以选择 HashMap;如果需要保持元素的有序状态,并且可以接受性能损耗,则可以选择 TreeMap;如果需要保持元素插入的顺序,并且具有较好的性能,则可以选择 LinkedHashMap。
-
-
队列(Queue):队列是一种**先进先出(FIFO)**的数据结构,Java 中的 Queue 接口有多种实现,如 LinkedList、PriorityQueue 等。
-
使用 LinkedList 类声明:
Queue<Type> queue = new LinkedList<>();
这是最常见的声明方式之一,使用 LinkedList 类作为 Queue 的实现。LinkedList 既可以作为 List,也可以作为 Queue 使用,因此它实现了 Queue 接口。
-
使用 PriorityQueue 类声明:
Queue<Type> queue = new PriorityQueue<>();
PriorityQueue 是一个优先级队列,它实现了 Queue 接口,并按照元素的自然顺序或者指定的比较器对元素进行排序。
相同点:
- 实现了 Queue 接口:LinkedList 和 PriorityQueue 都实现了 Queue 接口,因此它们都提供了队列的基本操作,如添加元素、移除元素、检查队首元素等。
- FIFO 原则:无论是 LinkedList 还是 PriorityQueue,它们都遵循先进先出 (FIFO) 的原则,即先进入队列的元素将被优先处理。
不同点:
- 底层数据结构不同:
- LinkedList 使用链表结构来实现队列,每个节点包含了对前一个节点和后一个节点的引用。因此,它适用于需要频繁进行插入和删除操作的场景,但在随机访问时性能相对较低。
- PriorityQueue 使用堆(通常是二叉堆)来实现队列,它可以保证队列中的元素按照优先级顺序进行排序。PriorityQueue 通常用于需要按照一定规则进行优先级处理的场景,例如任务调度、事件处理等。
- 元素排序:
- LinkedList 不对队列中的元素进行排序,元素的顺序与它们被添加到队列中的顺序相同。
- PriorityQueue 会根据元素的自然顺序或者指定的比较器对队列中的元素进行排序。元素按照优先级从高到低(或从低到高)顺序排列。
- 性能特点:
- LinkedList 适用于频繁进行插入和删除操作的场景,因为它在插入和删除操作时的时间复杂度为 O(1)。
- PriorityQueue 的插入和删除操作的时间复杂度为 O(log n),因为它要维护元素的优先级顺序。虽然 PriorityQueue 通常比较适合需要按照优先级处理元素的场景,但在一般情况下,其性能可能不如 LinkedList。
综上所述,选择使用 LinkedList 还是 PriorityQueue 取决于具体的需求:如果需要简单的先进先出队列,并且需要频繁进行插入和删除操作,则可以选择 LinkedList;如果需要按照优先级顺序处理元素,并且可以接受稍高的性能开销,则可以选择 PriorityQueue。
-
-
栈(Stack):栈是一种**后进先出(LIFO)**的数据结构,Java 中的 Stack 类实现了栈的功能。
-
使用 ArrayDeque 类声明:
Stack<Type> stack = new Stack<>();
这是最常见的声明方式之一。Stack 类是 Java 集合框架中的一部分,它继承自 Vector 类,但通常建议使用 ArrayDeque 来代替 Stack,因为 ArrayDeque 是一个更加通用的类,性能更好。
-
使用 LinkedList 类声明:
Stack<Type> stack = new Stack<>();
相同点:
- 实现了 Stack 接口:无论是 ArrayDeque 还是 LinkedList,它们都可以作为 Stack 的实现类,提供了栈的基本操作,如 push、pop、peek 等。
- 都支持动态扩容:无论是 ArrayDeque 还是 LinkedList,它们都可以动态地扩容以适应栈中元素的增加。
不同点:
- 底层数据结构不同:
- ArrayDeque 使用数组作为底层数据结构来实现栈。数组在内存中是连续存储的,因此支持随机访问,但在插入和删除元素时可能需要移动其他元素,尤其是在数组的开头进行操作时。
- LinkedList 使用链表作为底层数据结构来实现栈。链表中的每个节点都包含对下一个节点的引用,因此插入和删除操作的时间复杂度为 O(1),但随机访问的性能相对较低。
- 性能特点:
- ArrayDeque 的 push、pop、peek 等操作的时间复杂度都是 O(1),因为它是基于数组实现的,具有良好的随机访问性能。
- LinkedList 的 push、pop、peek 等操作的时间复杂度也是 O(1),因为它是基于链表实现的,但在实际应用中,LinkedList 可能会因为额外的指针开销和缓存未命中而稍微慢一些。
- 空间利用效率:
- ArrayDeque 在大多数情况下占用的空间更小,因为它不需要存储额外的指针来维护链表结构。
- LinkedList 需要额外的指针来维护链表结构,因此可能占用更多的空间。
综上所述,选择 ArrayDeque 还是 LinkedList 作为 Stack 的实现取决于具体的需求和场景:如果需要良好的随机访问性能以及较小的空间占用,可以选择 ArrayDeque;如果在插入和删除操作频繁的场景下性能更为重要,可以选择 LinkedList。
-