几种储存数据的方式

本文详细介绍了Java中数组、列表(List)、集合(Set和Map)、队列(Queue,如LinkedList和PriorityQueue)以及栈(Stack,如ArrayDeque和LinkedList)的声明、特点和适用场景。重点对比了这些数据结构在性能和功能上的异同。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

几种储存数据的方式

  1. 数组(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];
    
  2. 集合(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 方法获取元素等。

  3. 列表(List):列表是一种有序的集合,允许重复元素。Java 中的 List 接口有多种实现,如 ArrayList、LinkedList、Vector 等,其中 ArrayList 是最常用的。

    // 声明一个ArrayList列表
    List<String> list = new ArrayList<>();
    // 声明一个LinkedList列表
    List<String> list = new LinkedList<>();
    

    相同点:

    1. 实现了 List 接口:Vector、ArrayList 和 LinkedList 都实现了 List 接口,因此它们都是有序集合,可以按索引访问元素。
    2. 允许存储重复元素:这三种集合类都允许存储重复元素。
    3. 支持动态增长:当集合中的元素超出其初始容量时,它们都能够动态增长以容纳更多元素。

    不同点:

    1. 线程安全性
      • Vector 是线程安全的,它的方法都是同步的(synchronized),因此适合在多线程环境中使用。
      • ArrayList 和 LinkedList 不是线程安全的,因此在多线程环境中使用时需要额外的同步措施。
    2. 底层数据结构
      • ArrayList 底层是基于数组实现的,它通过数组来存储元素,因此支持快速的随机访问和遍历,但插入和删除操作可能会比较耗时,特别是在数组的中间位置。
      • LinkedList 底层是基于链表实现的,它通过链表来存储元素,因此插入和删除操作比较快,特别是在链表的中间位置,但随机访问和遍历可能会比较耗时。
    3. 性能差异
      • 对于随机访问和修改元素,ArrayList 的性能比 LinkedList 更好,因为 ArrayList 支持通过索引直接访问元素,时间复杂度为 O(1);而 LinkedList 需要遍历链表来查找元素,时间复杂度为 O(n)。
      • 对于插入和删除操作,尤其是在集合的中间位置,LinkedList 的性能比 ArrayList 更好,因为 LinkedList 只需要修改节点的指针,时间复杂度为 O(1),而 ArrayList 需要移动元素,时间复杂度为 O(n)。
  4. 集(Set):集是一种不允许重复元素的集合,主要实现类有 HashSet、TreeSet 等。

    Set<String> set1 = new HashSet<>(); // 使用 HashSet
    Set<Integer> set2 = new TreeSet<>(); // 使用 TreeSet
    Set<Double> set3 = new LinkedHashSet<>(); // 使用 LinkedHashSet
    

    相同点:

    1. 实现了 Set 接口:HashSet、TreeSet 和 LinkedHashSet 都实现了 Set 接口,因此它们都不允许包含重复元素,保持了集合元素的唯一性。
    2. 不保证元素顺序:HashSet 和 TreeSet 不保证元素的插入顺序,而 LinkedHashSet 则保留了元素插入的顺序。
    3. 不允许存储 null 元素:这三种集合类都不允许存储 null 元素,如果试图将 null 元素添加到集合中,将会抛出 NullPointerException 异常。

    不同点:

    1. 底层数据结构
      • HashSet 使用哈希表(HashMap)来存储元素,因此它的元素是无序的,但是插入、删除和查找操作的时间复杂度都是 O(1)。
      • TreeSet 使用红黑树(TreeMap)来存储元素,因此它能够保持元素的排序状态,插入、删除和查找操作的时间复杂度都是 O(log n),并且元素是有序的。
      • LinkedHashSet 使用哈希表和链表来存储元素,它保留了元素插入的顺序,并且插入、删除和查找操作的时间复杂度都是 O(1)。
    2. 性能和排序
      • HashSet 具有最好的性能,适合在大多数情况下使用,但不保证元素的顺序。
      • TreeSet 保持元素的排序状态,可以根据自然顺序或者指定的 Comparator 进行排序,但是由于使用了红黑树,性能相对于 HashSet 会有一些损耗。
      • LinkedHashSet 在 HashSet 的基础上保留了元素插入的顺序,并且性能接近于 HashSet。
    3. 迭代顺序
      • HashSet 的迭代顺序是不确定的,因为它是基于哈希表实现的。
      • TreeSet 的迭代顺序是有序的,根据元素的自然顺序或者指定的 Comparator 进行排序。
      • LinkedHashSet 的迭代顺序是元素插入的顺序,因为它使用链表来维护插入顺序。

    综上所述,选择适合的集合类取决于具体的需求:如果需要快速的插入、删除和查找操作,并且不关心元素的顺序,则可以选择 HashSet;如果需要保持元素的排序状态,并且可以接受性能损耗,则可以选择 TreeSet;如果需要保持元素插入的顺序,并且具有较好的性能,则可以选择 LinkedHashSet。

  5. 映射(Map):映射是一种键值对的集合,每个键都映射到一个值。Java 中的 Map 接口有多种实现,如 HashMap、TreeMap、LinkedHashMap 等。

    声明:

    1. 使用 HashMap 类声明

      Map<KeyType, ValueType> map = new HashMap<>();
      

      这种方式是最常见的声明 Map 的方式,使用 HashMap 类作为实现,在大多数情况下都可以满足需求。

    2. 使用 TreeMap 类声明

      Map<KeyType, ValueType> map = new TreeMap<>();
      

      TreeMap 是基于红黑树的实现,它可以保持元素的排序状态,按照键的自然顺序或者指定的 Comparator 进行排序。

    3. 使用 LinkedHashMap 类声明

      Map<KeyType, ValueType> map = new LinkedHashMap<>();
      

      LinkedHashMap 继承自 HashMap,它保留了元素插入的顺序,在迭代时可以按照元素插入的顺序访问元素。

      相同点:

      1. 实现了 Map 接口:HashMap、TreeMap 和 LinkedHashMap 都实现了 Map 接口,因此它们都用于存储键值对,并提供了对键值对进行增删改查操作的方法。
      2. 允许存储 null 键和 null 值:这三种 Map 都允许存储 null 键和 null 值,但需要注意的是,HashMap 中只能有一个 null 键,而 TreeMap 和 LinkedHashMap 中则可以有一个 null 键和多个 null 值。

      不同点:

      1. 底层数据结构不同
        • HashMap 使用哈希表(数组 + 链表/红黑树)来存储键值对,具有 O(1) 的常数时间复杂度的增删改查操作(在均匀散列的情况下)。
        • TreeMap 基于红黑树实现,可以保持键值对的有序状态,根据键的自然顺序或指定的比较器进行排序,因此它的键值对是有序的,但增删改查操作的时间复杂度是 O(log n)。
        • LinkedHashMap 继承自 HashMap,使用哈希表和双向链表来存储键值对,它保留了元素插入的顺序,因此可以按照插入顺序迭代元素,但性能接近于 HashMap。
      2. 性能和排序
        • HashMap 具有最好的性能,适合大多数情况下的使用,但不保证元素的顺序。
        • TreeMap 可以保持元素的有序状态,但由于使用了红黑树,性能相对于 HashMap 会有一些损耗。
        • LinkedHashMap 在 HashMap 的基础上保留了元素插入的顺序,并且性能接近于 HashMap。
      3. 迭代顺序
        • HashMap 的迭代顺序是不确定的,因为它是基于哈希表实现的。
        • TreeMap 的迭代顺序是根据键的自然顺序或指定的比较器进行排序的。
        • LinkedHashMap 的迭代顺序是元素插入的顺序,因为它使用链表来维护插入顺序。

      综上所述,选择适合的 Map 实现类取决于具体的需求:如果需要快速的增删改查操作,并且不关心元素的顺序,则可以选择 HashMap;如果需要保持元素的有序状态,并且可以接受性能损耗,则可以选择 TreeMap;如果需要保持元素插入的顺序,并且具有较好的性能,则可以选择 LinkedHashMap。

  6. 队列(Queue):队列是一种**先进先出(FIFO)**的数据结构,Java 中的 Queue 接口有多种实现,如 LinkedList、PriorityQueue 等。

    1. 使用 LinkedList 类声明

      Queue<Type> queue = new LinkedList<>();
      

      这是最常见的声明方式之一,使用 LinkedList 类作为 Queue 的实现。LinkedList 既可以作为 List,也可以作为 Queue 使用,因此它实现了 Queue 接口。

    2. 使用 PriorityQueue 类声明

      Queue<Type> queue = new PriorityQueue<>();
      

      PriorityQueue 是一个优先级队列,它实现了 Queue 接口,并按照元素的自然顺序或者指定的比较器对元素进行排序。

    相同点:

    1. 实现了 Queue 接口:LinkedList 和 PriorityQueue 都实现了 Queue 接口,因此它们都提供了队列的基本操作,如添加元素、移除元素、检查队首元素等。
    2. FIFO 原则:无论是 LinkedList 还是 PriorityQueue,它们都遵循先进先出 (FIFO) 的原则,即先进入队列的元素将被优先处理。

    不同点:

    1. 底层数据结构不同
      • LinkedList 使用链表结构来实现队列,每个节点包含了对前一个节点和后一个节点的引用。因此,它适用于需要频繁进行插入和删除操作的场景,但在随机访问时性能相对较低。
      • PriorityQueue 使用堆(通常是二叉堆)来实现队列,它可以保证队列中的元素按照优先级顺序进行排序。PriorityQueue 通常用于需要按照一定规则进行优先级处理的场景,例如任务调度、事件处理等。
    2. 元素排序
      • LinkedList 不对队列中的元素进行排序,元素的顺序与它们被添加到队列中的顺序相同。
      • PriorityQueue 会根据元素的自然顺序或者指定的比较器对队列中的元素进行排序。元素按照优先级从高到低(或从低到高)顺序排列。
    3. 性能特点
      • LinkedList 适用于频繁进行插入和删除操作的场景,因为它在插入和删除操作时的时间复杂度为 O(1)。
      • PriorityQueue 的插入和删除操作的时间复杂度为 O(log n),因为它要维护元素的优先级顺序。虽然 PriorityQueue 通常比较适合需要按照优先级处理元素的场景,但在一般情况下,其性能可能不如 LinkedList。

    综上所述,选择使用 LinkedList 还是 PriorityQueue 取决于具体的需求:如果需要简单的先进先出队列,并且需要频繁进行插入和删除操作,则可以选择 LinkedList;如果需要按照优先级顺序处理元素,并且可以接受稍高的性能开销,则可以选择 PriorityQueue。

  7. 栈(Stack):栈是一种**后进先出(LIFO)**的数据结构,Java 中的 Stack 类实现了栈的功能。

    1. 使用 ArrayDeque 类声明

      Stack<Type> stack = new Stack<>();
      

      这是最常见的声明方式之一。Stack 类是 Java 集合框架中的一部分,它继承自 Vector 类,但通常建议使用 ArrayDeque 来代替 Stack,因为 ArrayDeque 是一个更加通用的类,性能更好。

    2. 使用 LinkedList 类声明

      Stack<Type> stack = new Stack<>();
      

    相同点:

    1. 实现了 Stack 接口:无论是 ArrayDeque 还是 LinkedList,它们都可以作为 Stack 的实现类,提供了栈的基本操作,如 push、pop、peek 等。
    2. 都支持动态扩容:无论是 ArrayDeque 还是 LinkedList,它们都可以动态地扩容以适应栈中元素的增加。

    不同点:

    1. 底层数据结构不同
      • ArrayDeque 使用数组作为底层数据结构来实现栈。数组在内存中是连续存储的,因此支持随机访问,但在插入和删除元素时可能需要移动其他元素,尤其是在数组的开头进行操作时。
      • LinkedList 使用链表作为底层数据结构来实现栈。链表中的每个节点都包含对下一个节点的引用,因此插入和删除操作的时间复杂度为 O(1),但随机访问的性能相对较低。
    2. 性能特点
      • ArrayDeque 的 push、pop、peek 等操作的时间复杂度都是 O(1),因为它是基于数组实现的,具有良好的随机访问性能。
      • LinkedList 的 push、pop、peek 等操作的时间复杂度也是 O(1),因为它是基于链表实现的,但在实际应用中,LinkedList 可能会因为额外的指针开销和缓存未命中而稍微慢一些。
    3. 空间利用效率
      • ArrayDeque 在大多数情况下占用的空间更小,因为它不需要存储额外的指针来维护链表结构。
      • LinkedList 需要额外的指针来维护链表结构,因此可能占用更多的空间。

    综上所述,选择 ArrayDeque 还是 LinkedList 作为 Stack 的实现取决于具体的需求和场景:如果需要良好的随机访问性能以及较小的空间占用,可以选择 ArrayDeque;如果在插入和删除操作频繁的场景下性能更为重要,可以选择 LinkedList。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值