Java集合类中的List、Set和Map及其实现类的详细介绍

文章目录

集合类放在java的util包下面,集合类存放的都是对象的引用,而非对象本身,

常见的集合有三种:Set、List、Map

  • List和Set实现了Collection接口,List和Set本身也是接口,Map为独立接口。
  • List实现的类:ArrayList、Vector、LinkedList
  • Set实现的类:HashSet、LinkedHashSet、TreeSet、ArraySet
  • Map实现的类:HashMap、TreeMap、Hashtable

一、Collection接口

Collection接口是Java集合框架中的一个根接口,它代表了一组对象,称为元素。这个接口本身并不直接实现任何数据结构,而是定义了一些基本的操作方法,这些方法可以被所有实现了Collection接口的集合类所共享。

  • 添加元素
  • boolean add(E e): 将指定的元素添加到此集合中(可选操作)。
  • boolean addAll(Collection<? extends E> c): 将指定集合中的所有元素添加到此集合中(可选操作)。
  • 移除元素
  • boolean remove(Object o): 从此集合中移除指定元素的单个实例,如果存在的话(可选操作)。
  • boolean removeAll(Collection<?> c): 移除此集合中那些也包含在指定集合中的所有元素(可选操作)。
  • void clear(): 移除此集合中的所有元素(可选操作)。
  • 检查元素
  • boolean contains(Object o): 如果此集合包含指定的元素,则返回true。
  • boolean containsAll(Collection<?> c): 如果此集合包含指定集合中的所有元素,则返回true。
  • boolean isEmpty(): 如果此集合不包含元素,则返回true。
  • 获取集合大小
  • int size(): 返回此集合中的元素数(其容量)。
  • 遍历集合
  • Iterator iterator(): 返回在此集合元素上进行迭代的迭代器。

除了上述基本方法外,Collection接口还定义了其他一些可选操作,如转换为数组的方法Object[] toArray()和 T[] toArray(T[] a),以及确保此集合的可修改性的方法boolean retainAll(Collection<?> c)(仅保留此集合中也包含在指定集合中的元素)。

Java集合框架中提供了多种实现了Collection接口的类,如List、Set和Queue等。这些类各自具有不同的特性,如List是有序的,允许重复元素;Set是无序的,不允许重复元素;Queue则遵循FIFO原则。

1、List:有值,可重复,有序

1、ArrayList

ArrayList 是 Java 编程语言中的一个类,它实现了 List 接口,主要用于在内存中存储对象的动态数组。ArrayList 提供了一种灵活的方式来存储和操作对象列表,可以动态地调整其大小。

  • 动态数组: ArrayList 在内部使用数组来存储元素,但它能够动态地增长和缩小以适应元素数量的变化。当向 ArrayList 添加元素时,如果当前数组的大小不足以容纳新元素,ArrayList 会自动创建一个更大的数组,并将现有元素复制到新数组中。同样,当从 ArrayList 中删除元素时,如果数组的大小过大,它可能会缩小其大小以节省内存。
  • 随机访问: 由于 ArrayList 在内部使用数组,因此可以通过索引快速访问任意位置的元素。这使得 ArrayList 在需要频繁访问列表中元素的情况下非常高效。
  • 顺序存储: ArrayList 中的元素按照它们被添加到列表中的顺序存储。这意味着可以通过迭代列表来按顺序访问元素。
  • 失败快速: ArrayList 在大多数情况下的性能都非常好,尤其是在随机访问和尾部插入或删除操作方面。然而,在列表的开头插入或删除元素时,由于需要将所有后续元素向前或向后移动一位,因此性能可能较差。
  • 线程不安全: ArrayList 不是线程安全的。如果多个线程同时修改 ArrayList,可能会导致数据不一致或其他并发问题。在多线程环境中使用 ArrayList 时,需要额外的同步措施来确保线程安全。
  • 容量和大小: ArrayList 有两个重要的属性:容量(capacity)和大小(size)。容量是 ArrayList 当前分配的存储空间大小,而大小是实际存储在 ArrayList 中的元素数量。可以通过调用 ensureCapacity() 方法来增加容量,但请注意,这并不会改变 ArrayList 的大小。同样,可以通过调用 add() 方法来增加大小,如果这导致容量不足,ArrayList 会自动增加其容量。

ArrayList 是一个功能强大且灵活的集合类,适用于需要在内存中存储和操作对象列表的场景。

在选择使用 ArrayList 时,需要考虑到其线程不安全性和在列表开头进行插入/删除操作时的性能问题。

2、Vector

Vector是一个表示可以改变大小的数组的序列容器,类似于数组,但具有动态调整大小的能力。

  • 动态数组: Vector可以动态地增长和缩小以适应元素的添加和删除。当需要添加新元素而当前空间不足时,Vector会自动重新分配更大的存储空间,并将现有元素移至新的存储空间。
  • 顺序序列: Vector中的元素按照严格的线性顺序排列,可以通过元素在序列中的位置(即下标)来访问对应的元素。
  • 高效随机访问: Vector提供了常数时间复杂度(O(1))的随机访问能力,可以快速访问任意位置的元素。
  • 自动内存管理: Vector自动处理其存储空间的分配和释放,无需手动管理内存。

Vector在多个领域都有广泛的应用:

  • 动态数据集合: 当数据集的大小不确定或会随时间变化时,Vector是理想的选择。例如,处理用户输入或读取文件数据时,Vector可以根据需要动态地增长。
  • 替代数组: 在编程中,Vector通常被用来替代传统的固定大小数组,因为它更加灵活且自动管理内存。
  • 数学和科学计算: 在科学计算、物理模拟、数学建模等领域中,Vector用于存储和操作大量数值数据,如矩阵的行或列。
  • 游戏开发和图形处理: Vector可用于存储游戏对象、粒子、坐标点等动态集合,以及图形处理程序中的像素数据、顶点信息、纹理坐标等。

Vector是一个功能强大且灵活的容器类,适用于需要动态存储和访问元素序列的场景。

由于Vector在添加或删除元素时可能需要重新分配存储空间,因此在某些情况下可能会带来一定的性能开销。因此,在选择使用Vector时,需要根据具体的应用场景和需求进行权衡。

3、LinkedList

LinkedList是一个基于链表实现的动态数据结构,它的大小可以在运行时根据需要进行调整。

LinkedList通过节点来存储数据,每个节点不仅包含数据元素,还包含指向前后节点的指针,这种结构使得LinkedList在插入和删除元素时具有非常高的性能。

  • 动态大小: 当元素数量超过当前容量时,LinkedList会自动扩展其大小,无需预先分配固定大小的空间。
  • 高效的插入和删除: 由于LinkedList中的元素是通过指针相互连接的,因此在插入或删除元素时,只需要修改相关节点的指针,而不需要动大量元素。这使得LinkedList在插入和删除操作上的性能优于某些其他数据结构,如ArrayList。
  • 较低的随机访问性能: LinkedList不支持像数组那样的直接索引访问,而是需要从链表头部或尾部遍历到指定的位置。因此,当需要频繁访问链表中间的元素时,LinkedList的性能可能不如ArrayList。
  • 灵活的使用方式: LinkedList可以作为栈、队列或双端队列来使用,这使得它在处理各种数据结构问题时具有很高的灵活性。
  • 非线程安全: 与ArrayList类似,LinkedList也是非线程安全的。如果需要在多线程环境中使用LinkedList,需要额外的同步措施来确保线程安全。

LinkedList是一个功能强大且灵活的数据结构,适用于需要频繁进行插入和删除操作,但对随机访问性能要求不高的场景。

在选择使用LinkedList时,需要考虑到其随机访问性能较低的特点,并结合具体的应用需求进行权衡。

2、Set:无序,值不可重复

1、HashSet

HashSet是Java中的一个重要集合类,它实现了Set接口,主要用于存储不重复的元素。HashSet内部使用HashMap来实现,因此它同样具有高效的性能表现。

  • 不重复元素: HashSet不允许存储重复的元素。当你试图将一个已经存在于集合中的元素添加到HashSet时,添加操作将不会有任何效果,即该元素不会被重复添加。
  • 无序性: HashSet不保证元素的迭代顺序与它们的插入顺序一致。也就是说,每次遍历HashSet时,元素的顺序可能会有所不同。
  • 基于HashMap实现: HashSet内部实际上是通过HashMap来实现的。每个元素都作为HashMap的一个键(key),而值(value)则是一个固定的对象(例如,HashSet内部使用了一个名为PRESENT的常量对象作为值)。这种实现方式使得HashSet在添加、删除和查找元素时都能保持较高的性能。
  • 快速查找: 由于HashSet基于HashMap实现,因此它具有常数时间复杂度(O(1))的查找性能。这意味着无论集合中包含多少元素,查找一个特定元素所需的时间都是固定的。
  • 自动扩容: 当HashSet中的元素数量超过当前容量时,它会自动进行扩容,以确保能够容纳更多的元素。这种动态扩容的特性使得HashSet在处理大量数据时非常灵活和高效。
  • 非线程安全: 与ArrayList和LinkedList一样,HashSet也是非线程安全的。如果需要在多线程环境中使用HashSet,需要额外的同步措施来确保线程安全。

HashSet是一个用于存储不重复元素的高效集合类,适用于需要快速查找和添加元素,且不关心元素顺序的场景。

在使用HashSet时,需要注意其非线程安全性的特性,并在必要时采取适当的同步措施。

2、LinkedHashSet

LinkedHashSet是Java中的一个集合类,它是HashSet的子类,并基于LinkedHashMap实现。LinkedHashSet的主要特点是既保持了元素的插入顺序,又保证了元素的唯一性。

  • 有序性: LinkedHashSet继承了HashSet的唯一性特性,同时增加了有序性。它使用双向链表来维护元素的插入顺序,确保在迭代时元素会按照它们被添加到集合中的顺序返回。这种有序性使得LinkedHashSet在需要按照特定顺序处理元素时非常有用。
  • 唯一性: 与HashSet一样,LinkedHashSet也保证集合中元素的唯一性。它使用元素的hashCode()方法和equals()方法来检查是否存在重复元素,并在添加时自动排除重复项。
  • 基于LinkedHashMap实现: LinkedHashSet内部实际上是通过LinkedHashMap来实现的。每个元素都作为LinkedHashMap的一个键(key),而值(value)则是一个固定的对象(例如,LinkedHashSet内部使用了一个名为PRESENT的常量对象作为值)。这种实现方式不仅保留了元素的插入顺序,还继承了HashMap的高效性能。
  • 高效性能: 由于LinkedHashSet基于LinkedHashMap实现,它继承了HashMap的高性能特点。在添加、删除和查找元素时,LinkedHashSet都能保持较高的性能,特别是在处理大量数据时表现尤为出色。
  • 动态扩容: 当LinkedHashSet中的元素数量超过当前容量时,它会自动进行扩容,以确保能够容纳更多的元素。这种动态扩容的特性使得LinkedHashSet在处理大量数据时非常灵活和高效。
  • 非线程安全: 与HashSet等其他集合类一样,LinkedHashSet也是非线程安全的。如果需要在多线程环境中使用LinkedHashSet,需要额外的同步措施来确保线程安全。

LinkedHashSet的特点主要体现在有序性、唯一性、高效性能和动态扩容等方面。它适用于需要按照插入顺序存储并处理唯一元素的场景,特别是在对性能要求较高的应用中表现出色。然而,在使用LinkedHashSet时,需要注意其非线程安全性的特性,并在必要时采取适当的同步措施。

LinkedHashSet是一个结合了有序性和唯一性的高效集合类,适用于需要按照插入顺序存储并处理唯一元素的场景。

3、TreeSet

TreeSet是Java集合框架中的一种有序集合,它实现了Set接口,因此具有不允许重复元素的特性。TreeSet通过红黑树数据结构来存储元素,这使得元素在集合中保持有序。

TreeSet中的元素支持两种排序方式:自然排序和根据创建TreeSet时提供的Comparator进行排序。自然排序是根据元素的自然顺序来排列,而Comparator排序则是根据提供的比较器来定义元素的顺序。

  • 有序性: TreeSet中的元素按照特定的顺序排列,这使得遍历TreeSet得到的元素是按照一定的顺序返回的。这种有序性为应用场景下的迭代和顺序处理提供了便利。
  • 唯一性: 与HashSet一样,TreeSet也保证元素的唯一性,不允许重复元素。
  • 高效的查找、插入、删除操作: 由于TreeSet基于红黑树实现,其查找、插入和删除操作的时间复杂度为O(log(n)),因此适用于大批量数据的操作。
  • 支持范围查找: TreeSet支持返回第一个大于等于或小于等于给定值的元素,这能满足一些特殊业务需求。
  • 线程安全: TreeSet具有底层数据结构红黑树的特性,包括平衡、高效以及线程安全。

TreeSet还继承了AbstractSet抽象类和实现了NavigableSet、Cloneable、java.io.Serializable接口,因此它具有Set的属性和方法,支持一系列的导航方法,能被克隆,并支持序列化。

TreeSet是一个有序的、唯一的元素集合,适用于需要有序存储唯一元素的场景。

在使用TreeSet时,需要注意其元素的排序方式以及线程安全性的考虑。

5、Queue:队列

Queue与List、Set同一级别,都是继承了Collection接口,LinkedList即可以实现Queue接口,也可以实现List接口,若实现Queue接口,会窄化LinkedList的方法的访问权限:即不能直接访问LinkedList的非Queue的方法。

Queue(队列)是一种特殊类型的线性数据结构,它遵循FIFO(First In First Out,先入先出)的原则,即最早加入的元素将是最先被移除的元素。Queue提供了在表的前端进行删除操作,而在表的后端进行插入操作的能力。

  • 入队(Enqueue): 在队列的尾部添加一个元素。
  • 出队(Dequeue): 移除队列的头部元素,并返回该元素。如果队列为空,则无法进行出队操作。
  • 查看队首(Peek): 返回队列头部的元素,但不移除它。如果队列为空,则返回特定值(如null或抛出异常)。
  • 检查队列是否为空(IsEmpty): 检查队列中是否还有元素。
  • 获取队列大小(Size): 返回队列中当前元素的数量。

Queue在实际应用中有许多用途,特别是在需要按照特定顺序处理元素的情况下。例如,在多线程编程中,Queue经常被用作线程之间的通信桥梁,一个线程可以将任务或数据放入队列,另一个线程可以从队列中取出任务或数据进行处理。此外,在计算机系统的许多其他方面,如网络数据包的处理、任务调度等,Queue都发挥着重要的作用。

Java标准库提供了几种Queue的实现,包括LinkedList、PriorityQueue、ArrayBlockingQueue等,它们各自具有不同的特性和适用场景。

二、Map接口

Map接口是Java编程中非常重要的一个接口,它用于存储键值对(key-value pair)的映射关系。Map接口提供了一种通过键来查找值的方式,其中键和值都可以是任意的Java对象。

Map接口定义了一系列用于操作映射关系的方法,例如添加、删除、更新和查询元素等。它允许用户根据键快速查找对应的值。Map中的键必须是唯一的,每个键最多映射到一个值。 此外,Map接口的实现类可以存储任意类型的键值对,这为用户提供了很大的灵活性。

在Java中,Map接口有多个实现类,如HashMap、Hashtable、TreeMap和LinkedHashMap等。

Map接口本身并不直接支持遍历操作,但可以通过keySet()或entrySet()方法将其转换为Set,从而进行遍历。此外,Map中的键不允许为null(HashMap和LinkedHashMap允许一个null键),而值可以为null(HashMap和LinkedHashMap允许多个null值,但TreeMap不允许null键或null值)。

Map接口为Java编程提供了强大的键值对存储和查找功能,能够方便地处理各种映射关系。

1、HashMap

HashMap是Java中Map接口的一个常用实现类,它基于哈希表数据结构来实现键值对的存储和查找。

  • 高效性能: HashMap通过哈希表来实现元素的存储和查找,因此具有非常高效的性能。在理想情况下,HashMap的查找、插入和删除操作的时间复杂度都是O(1),即常数时间复杂度。这使得HashMap在处理大量数据时能够保持出色的性能。
  • 无序性: HashMap不保证元素的顺序,即遍历HashMap时,元素的顺序可能与插入的顺序不一致。如果需要保持元素的顺序,可以考虑使用LinkedHashMap。
  • 允许null键和null值: HashMap允许键(key)和值(value)为null,但键只能有一个为null,值可以有多个为null。这一特性使得HashMap在某些场景下非常方便。
  • 动态扩容: 当HashMap中的元素数量超过当前容量的一定阈值时,HashMap会自动进行扩容,以容纳更多的元素。扩容过程中会重新计算哈希值并移动元素,因此扩容操作可能会涉及到较大的性能开销。为了避免频繁扩容,可以在创建HashMap时指定一个合适的初始容量和加载因子。
  • 冲突处理: 由于哈希表的特性,不同的键可能会计算出相同的哈希值,从而导致冲突。HashMap通过链表或红黑树来解决冲突。当冲突较少时,HashMap使用链表来存储具有相同哈希值的键值对;当冲突较多时,链表会转换为红黑树以提高查找性能。

在使用HashMap时,需要注意以下几点:

  • 键(key)必须是唯一的,不能重复。 如果插入具有相同键的键值对,后插入的值会覆盖先插入的值。
  • 尽量避免使用可变对象作为键。 因为HashMap在存储键值对时会计算键的哈希值,并在后续操作中依赖这个哈希值。如果键对象在HashMap中发生变化,其哈希值也可能发生变化,导致HashMap无法正确查找或删除元素。
  • 当HashMap中的元素数量较多时,扩容操作可能会带来较大的性能开销。 因此,在创建HashMap时,最好根据预期的元素数量来指定一个合适的初始容量和加载因子,以减少扩容次数。
1、HashMap结构

HashMap是链表散列的数据结构,即数组和链表的结合体,底层是一个数组结构,而数组结构中的每一项元素又是一个链表结构,即Entry<k, v>,Entry就是数组中的元素,每个Map.Entry其实就是一个key-value键值对,它持有指向下一个的引用。

  • ① --> Entry<k, v>
  • ② --> Entry<k, v> --> Entry<k, v>
  • ③ --> Entry<k, v>
  • ④ ...
  • ⑤ --> Entry<k, v> --> Entry<k, v> --> Entry<k, v>

解释:

  • 数组:HashMap以键值对的方式存储结构,其中key-value都是Map.Entry中的属性。数组的下标对应key值,数组的值对应value。
  • 对应的过程:将key值进行Hash后得到Hash值,若Hash值的范围大与数组长度的话,就需要转换,对数组的长度进行取模运算,得到的余数即为存放的下标key,HashMap的长度一般是2的幂次方,是为了提高模运算的效率。
  • 链表:经过上述操作,会出现相同的下标key,即产生了Hash冲突,采用链表的方式进行存储,如果key相同,则覆盖原始值。如果key出现冲突,就将当前的key-value值放入链表,进一步对比value值,不同,则放入链表的下一个节点。

Hash取模详细解释:

  • 通过put()方法传递键key和值value时,先对键调用hashCode方法,计算并返回hashCode值用于找到Map数组的位置来存储Entry对象,hashCode方法是根据key的hash值来求对应数组中的位置。使用hashCode对数组的长度进行取模运算,元素尽量分布的均匀一点,取模后取得相同的值即为hash冲突。取模相同的要使用equals方法去判断,相同的话不存储,不相同的话就存储到链表的下一个节点。

2、Hashtable

Hashtable是Java中基于哈希表(Hash Table)实现的一种数据结构,用于存储键值对(key-value pair)。Hashtable的主要目的是提供一种快速查找和存储数据的方式。它通过哈希函数将键映射到存储位置,从而实现了高效的插入、删除和查找操作。

  • 唯一键: Hashtable中的键(key)必须是唯一的,每个键最多映射到一个值(value)。这意味着如果你尝试插入具有相同键的新键值对,那么新值会覆盖旧值。
  • 线程安全: Hashtable是线程安全的,这意味着在多线程环境中,多个线程可以同时读写Hashtable而不会导致数据不一致。然而,线程安全也带来了一定的性能开销。如果需要更高的性能,并且可以接受一定程度的线程不安全,可以考虑使用HashMap。
  • 性能: 通过哈希函数,Hashtable可以快速定位到存储的数据位置,因此查找速度快。同时,插入和删除操作也相对高效,因为只需要计算一次哈希函数即可。
  • 冲突处理: 哈希函数并不是完美的,有时会出现多个键映射到同一个位置的情况,这种情况称为哈希冲突。为了解决哈希冲突问题,Hashtable通常采用拉链法或开放寻址法。

虽然Hashtable提供了线程安全,但在高并发的场景下,其性能可能不如其他非线程安全的哈希表实现(如HashMap)。此外,由于Hashtable是较老的Java集合类,一些新的Java特性(如泛型)在Hashtable中并未得到很好的支持。

3、TreeMap

TreeMap是Java中的一种集合类,它实现了SortedMap接口,这意味着TreeMap中的元素可以按照键的自然顺序或者自定义顺序进行排序。TreeMap的底层实现采用了红黑树(Red-Black Tree)的数据结构,这种数据结构能够确保TreeMap在动态插入、删除和修改键值对时,仍然能够保持元素的有序性。

  • 排序: TreeMap中的键值对按照键的顺序进行排序。默认情况下,它按照键的自然顺序进行排序,但也可以通过构造函数传入自定义的Comparator来实现特定的排序规则。
  • 有序性: 由于TreeMap是按照键的顺序存储元素的,因此在遍历时可以按照排序顺序获取或操作元素。这种有序性使得TreeMap在处理需要排序的场景时非常有用。
  • 动态更新: TreeMap支持动态插入、删除和修改键值对操作,这些操作会保持元素的有序性。也就是说,无论何时进行更新操作,TreeMap都能够保持其内部元素的排序状态。
  • 范围查询: TreeMap提供了一系列的方法来支持范围查询,例如headMap、tailMap和subMap等。这些方法可以根据指定的范围获取子映射,使得在处理需要按照键的范围来查询和操作数据的场景时非常高效。

TreeMap主要适用于需要排序和范围查询的场景。例如,当需要按照键的顺序访问和处理数据时,可以使用TreeMap来存储键值对,并利用其排序特性方便地进行相关操作。此外,当需要根据键的范围来查询和操作数据时,TreeMap提供的范围查询方法能够快速地定位所需的子映射。

在使用TreeMap时,需要注意自定义比较器的情况。如果元素不实现Comparable接口,或者需要按照不同的方式进行排序,可以通过构造函数传入自定义的Comparator来实现特定的排序规则。同时,为了保证线程安全,可以使用Collections.synchronizedSortedMap进行包装,或者使用ConcurrentSkipListMap作为线程安全的替代。

4、LinkedHashMap

LinkedHashMap是Java中的一种特殊类型的HashMap,它继承自HashMap并实现了Map接口。与普通的HashMap不同,LinkedHashMap在内部使用双向链表维护了插入顺序或者访问顺序,从而保证了元素的顺序。

  • 有序性: LinkedHashMap按照元素的插入顺序或访问顺序进行排序和存储,因此,在迭代LinkedHashMap时,元素会按照它们被插入或访问的顺序返回。这种有序性使得LinkedHashMap非常适合于需要保持元素顺序的场景。
  • 高效性: 由于LinkedHashMap底层使用哈希表实现,因此它具有HashMap的高效查找和访问特性。可以快速进行元素的查找、插入和删除操作。
  • 可预测性: 由于LinkedHashMap维护了元素的顺序,因此它提供了可预测的遍历顺序。这在某些需要按照特定顺序处理元素的场景中非常有用。

LinkedHashMap的用途广泛,例如在需要实现LRU(Least Recently Used,最近最少使用)缓存的场景中,LinkedHashMap可以根据元素的访问顺序进行缓存淘汰。此外,它还可以用于实现有序的映射表,保持元素按照插入顺序或访问顺序排列。

需要注意的是,虽然LinkedHashMap保持了元素的顺序,但在多线程环境下使用时,仍然需要注意线程安全问题。如果需要在多线程环境中使用LinkedHashMap,可以考虑使用其线程安全的版本,或者使用其他线程安全的集合类。

三、List、Set、Map是否可以为null值

1、List的实现类

  • 大部分List的实现类(如ArrayList, LinkedList等)都允许存储null值作为列表的元素。例如,你可以向ArrayList中添加一个null元素。
  • List本身(即List类型的引用)也可以被设置为null。这取决于你如何初始化或赋值给这个引用。

2、Set的实现类

  • Set的实现类(如HashSet, TreeSet等)一般不允许存储重复的元素。但是,它们是否可以存储null值取决于具体的实现。例如,HashSet允许存储一个null元素,但TreeSet不允许(因为TreeSet需要元素之间可比较,而null不能参与比较)。
  • Set本身(即Set类型的引用)也可以被设置为null。

3、Map的实现类

  • Map的实现类(如HashMap, TreeMap, Hashtable等)对于键和值的处理各不相同。例如,HashMap允许键和值都可以是null。TreeMap不允许null键,但值可以是null。Hashtable则不允许键或值为null。
  • Map本身(即Map类型的引用)同样可以被设置为null。

4、总结

  • List允许存储的值为null,并且可以存储多个,不会对数据结构造成影响。
  • HashSet、LinkedHashSet底层为HashMap,可以有1个为null的元素。
  • TreeSet不能有key为null的元素,需要进行元素之间的比较,会报空指针错误。
  • HashMap只能有一个key为null的节点,因为Map的key相同时,后面的节点会替换之前的key。
  • TreeMap会调用CompareTo方法,对象为null时会报空指针错误。
  • Hashtable底层为散列表,无论是key为null或者value为null,都会报错。
相关推荐
wkj0017 分钟前
接口实现类向上转型和向上转型解析
java·开发语言·c#
qqxhb8 分钟前
零基础设计模式——行为型模式 - 观察者模式
java·观察者模式·设计模式·go
寒士obj35 分钟前
类加载的过程
java·开发语言
无名之逆38 分钟前
大三自学笔记:探索Hyperlane框架的心路历程
java·开发语言·前端·spring boot·后端·rust·编程
Chuck1sn40 分钟前
我把 Cursor AI 整合到 Ruoyi 中,从此让 Java 脚手架脱离人工!
java·vue.js·后端
水木石画室43 分钟前
Spring Boot 常用注解面试题深度解析
java·spring boot·后端
hweiyu001 小时前
tomcat指定使用的jdk版本
java·开发语言·tomcat
百锦再1 小时前
.NET 类库开发详细指南c
java·log4j·.net·net·dot
黎䪽圓2 小时前
【Java多线程从青铜到王者】阻塞队列(十)
java·开发语言