Java容器学习,一文读懂Java容器使用知识文集。

在这里插入图片描述

🏆作者简介,普修罗双战士,一直追求不断学习和成长,在技术的道路上持续探索和实践。
🏆多年互联网行业从业经验,历任核心研发工程师,项目技术负责人。
🎉欢迎 👍点赞✍评论⭐收藏

文章目录

    • 🔎 一、Java高频容器知识文集(1)
      • 🍁🍁 01. Java 容器都有哪些?之间有什么区别?
      • 🍁🍁 02. Collection 和 Collections 有什么区别?
      • 🍁🍁 03. List、Set、Map 之间的区别是什么?举例说明?
      • 🍁🍁 04. HashMap 和 Hashtable 有什么区别?
      • 🍁🍁 05. 如何决定使用 HashMap 还是 TreeMap?
      • 🍁🍁 06. ArrayList 和 LinkedList 的区别是什么?
      • 🍁🍁 07. 如何实现数组和 List 之间的转换?
      • 🍁🍁 08. ArrayList 和 Vector 的区别是什么?
      • 🍁🍁 09. Array 和 ArrayList 有何区别?
      • 🍁🍁 10. 在 Queue 中 poll()和 remove()有什么区别?
      • 🍁🍁 11. 哪些集合类是线程安全的?
      • 🍁🍁 12. 迭代器 Iterator 是什么?
      • 🍁🍁 13. Iterator 怎么使用?有什么特点?
      • 🍁🍁 14. Iterator 和 ListIterator 有什么区别?
      • 🍁🍁 15. 怎么确保一个集合不能被修改?
      • 🍁🍁 16. 如何实现数组和 List 之间的转换?
      • 从数组转换为 List:
      • 从 List 转换为数组:
      • 🍁🍁 17. comparable 和 comparator的区别?
      • 🍁🍁 18. HashMap是如何解决哈希冲突的?
      • 🍁🍁 19. 说一下 HashMap 的实现原理?
      • 🍁🍁 20. 说一下 HashSet 的实现原理?

🔎 一、Java高频容器知识文集(1)

🍁🍁 01. Java 容器都有哪些?之间有什么区别?

在 Java 中,容器通常指的是常用的数据结构集合,Java 提供了多种容器来存储、操作和访问数据。主要的 Java 容器包括以下几种:

  1. List(列表)

    • 以特定顺序存储元素的集合,允许重复元素。常见的实现类有 ArrayList、LinkedList、Vector 等。
    • 举例:
      List<String> arrayList = new ArrayList<>();
      List<Integer> linkedList = new LinkedList<>();
      
  2. Set(集合)

    • 不包含重复元素的集合。常见的实现类有 HashSet、LinkedHashSet、TreeSet 等。
    • 举例:
      Set<String> hashSet = new HashSet<>();
      Set<Integer> treeSet = new TreeSet<>();
      
  3. Map(映射)

    • 存储键值对的集合,每个键最多只能映射到一个值。常见的实现类有 HashMap、LinkedHashMap、TreeMap 等。
    • 举例:
      Map<String, Integer> hashMap = new HashMap<>();
      Map<String, String> linkedHashMap = new LinkedHashMap<>();
      
  4. Queue(队列)

    • 用于存储元素,并以特定顺序提供对元素的访问。常见的实现类有 PriorityQueue、LinkedList 等。
    • 举例:
      Queue<String> priorityQueue = new PriorityQueue<>();
      Queue<Integer> linkedListQueue = new LinkedList<>();
      
  5. Stack(栈)

    • 后进先出(LIFO)的数据结构。常见的实现类有 Stack 类。
    • 举例:
      Stack<String> stack = new Stack<>();
      

上述是 Java 中常用的容器,它们都提供了不同的特性和适用场景,可以根据需求选择合适的容器来存储和操作数据。

下面是一个以表格形式说明Java中不同容器之间区别的例子:

容器类型是否允许重复元素是否有序是否按键排序是否基于新元素的值进行排序
List允许有序N/AN/A
Set不允许无序N/AN/A
Map允许(值不重复)无序键排序值排序
Queue允许有序N/AN/A
Stack允许有序N/AN/A

请注意,表中的排序概念只适用于实现了特定排序接口的容器。

  • List 允许重复元素,维护插入顺序,并且可以通过索引访问元素。
  • Set 不允许重复元素,并且不保证元素的顺序。
  • Map 以键值对的形式存储元素,键值对可以有重复的值,但键唯一。可以按照键或值的顺序进行排序。
  • Queue 用于存储元素,并且提供对元素的访问顺序。
  • Stack 是一种后进先出(LIFO)的数据结构。

根据不同的需求和使用场景,可以选择适当的容器类型来满足需求。

🍁🍁 02. Collection 和 Collections 有什么区别?

CollectionCollections 是 Java 中的两个不同的概念:

  1. Collection

    • Collection 是 Java 集合框架的根接口,它代表一组对象,是集合类的基本接口。Collection 接口派生了 List、Set 和 Queue 等子接口,它定义了对集合元素进行基本操作的方法,比如添加、删除、遍历等。
    • 举例:
      Collection<String> list = new ArrayList<>();
      list.add("element1");
      list.add("element2");
      
  2. Collections

    • Collections 是 Java 实用类库中的一个工具类,包含各种静态方法用于操作集合,比如对集合进行排序、查找最大最小值、反转等操作。它提供了一些实用的静态方法,用于操作各种集合类。
    • 举例:
      List<Integer> numbers = new ArrayList<>();
      numbers.add(3);
      numbers.add(1);
      numbers.add(2);
      Collections.sort(numbers);
      

因此,Collection 是表示集合类的根接口,而 Collections 是包含用于操作集合的静态方法的实用类。它们的区别在于一个是接口用于表示集合操作的基本方法,一个是工具类用于提供集合操作的实用方法。

下面是一个以表格形式说明CollectionCollections之间区别的例子:

区别CollectionCollections
定义根接口,表示一组对象的集合工具类,提供对集合的操作方法
功能定义了集合基本操作的方法,如添加、删除等提供了各种集合操作的静态方法
包含的接口List、Set、Queue 等集合接口的父接口
是否包含实现
使用方式通常需要具体的实现类实例化对象作为静态工具类直接调用方法

请注意,Collection 是一个接口,而 Collections 是一个包含静态方法的工具类。它们的主要区别在于功能和用法。

  • Collection 是一组对象的集合的根接口,定义了集合的基本操作方法,如添加、删除、遍历等。它是其他集合接口的父接口,需要使用具体的实现类来实例化对象。
  • Collections 是一个工具类,提供了很多静态方法用于操作集合,如排序、查找、反转等操作。它包含一些常用的算法和方法,可以直接调用,而不需要实例化对象。

因此,CollectionCollections 在功能和使用方式上存在明显的区别,一个是表示集合类的接口,一个是提供集合操作的工具类。

🍁🍁 03. List、Set、Map 之间的区别是什么?举例说明?

List、Set 和 Map 是 Java 集合框架中常见的三种接口,它们之间有以下主要区别:

  1. List

    • List 是一个有序集合,允许存储重复元素。它可以根据索引访问元素,元素在列表中的位置是有意义的。
    • 举例:ArrayList 和 LinkedList 都是 List 接口的实现,允许存储重复元素,并且可以按照元素的插入顺序访问元素。
  2. Set

    • Set 是一个不允许有重复元素的集合,元素没有特定的顺序。如果试图向 Set 中添加重复元素,添加操作将被忽略。
    • 举例:HashSet 和 TreeSet 都是 Set 接口的实现,不允许存储重复元素,并且不保证元素的顺序。
  3. Map

    • Map 是一种键值对的集合,每个键只能对应一个值。键是唯一的,值可以重复。Map 不属于 Collection 接口的一部分,它提供了根据键查找值的功能。
    • 举例:HashMap 和 TreeMap 都是 Map 接口的实现,用于将键映射到值,并允许通过键来查找对应的值。

综上所述,List 是有序集合、允许存储重复元素,可以根据索引访问元素;Set 是不允许有重复元素的集合,不保证元素顺序;Map 是键值对的集合,键唯一对应值。根据实际需求,选择合适的集合类型可以更好地满足需求。

下面是一个表格,说明 List、Set 和 Map 之间的区别:

ListSetMap
顺序有序无序无序
元素重复性允许不允许键不允许重复,值允许重复
访问方式通过索引访问无法通过索引访问通过键值对访问
接口Collection 接口的子接口Collection 接口的子接口不属于 Collection 接口的一部分
例子ArrayList、LinkedList 等HashSet、TreeSet 等HashMap、TreeMap 等

该表格总结了List、Set和Map之间的主要区别,包括顺序特性、元素重复性、访问方式、以及它们所实现的不同接口等方面。选择合适的集合类型可以更好地满足具体的编程需求。

🍁🍁 04. HashMap 和 Hashtable 有什么区别?

HashMap 和 Hashtable 都是用于存储键值对的数据结构,它们之间有以下区别:

  1. 线程安全性:

    • HashMap 是非线程安全的,不支持多线程并发操作,如果需要在多线程环境下使用,需要额外的同步机制来保证线程安全。
    • Hashtable 是线程安全的,它的方法都是同步的,可以直接在多线程环境中使用,但也因此在单线程环境下性能会有所下降。
  2. 空键值(null)的处理:

    • HashMap 允许将键和值均设置为 null,即允许存在键或值为 null 的情况。
    • Hashtable 不允许键或值为 null,如果存储 null 值,会直接抛出 NullPointerException。
  3. 继承关系:

    • HashMap 继承自 AbstractMap 类,实现了 Map 接口,是 Java Collections Framework 中的一部分。
    • Hashtable 继承自 Dictionary 类,实现了 Map 接口,但已经被标记为"过时",推荐使用 ConcurrentHashMap 来替代。
  4. 性能:

    • 由于线程安全机制的不同,HashMap 在非多线程环境下性能通常优于 Hashtable。在多线程环境下,ConcurrentHashMap 是更好的选择。
  5. 迭代器:

    • HashMap 的迭代器是 fail-fast 的,即在迭代过程中若其他线程对其进行结构性修改,会抛出 ConcurrentModificationException 异常。
    • Hashtable 的迭代器是 fail-safe 的,允许在迭代过程中对集合进行修改,不会抛出异常。

基于以上区别,通常来说,在新的代码中建议使用 HashMap 而不是 Hashtable。HashMap 提供了更好的性能,在单线程环境下更适用,并且允许存储 null 键和值。在多线程环境下,推荐使用 ConcurrentHashMap 来替代 Hashtable。

以下是一个表格,说明了 HashMap 和 Hashtable 之间的区别:

特性HashMapHashtable
线程安全性非线程安全线程安全
空键值(null)允许键和值均为 null不允许键或值为 null
继承关系继承自 AbstractMap 类继承自 Dictionary 类
替代方案在非多线程环境下通常优于 Hashtable已被标记为"过时",推荐使用 ConcurrentHashMap
性能通常在非多线程环境下性能优于 Hashtable在单线程环境下性能有所下降,在多线程环境下相对较低
迭代器fail-fastfail-safe

该表格总结了 HashMap 和 Hashtable 之间的主要区别,可以帮助开发人员根据实际需求选择合适的数据结构。

🍁🍁 05. 如何决定使用 HashMap 还是 TreeMap?

决定使用 HashMap 还是 TreeMap 取决于几个因素:

  1. 排序需求:如果你需要按键的自然顺序(字典顺序)来进行排序,那么应该使用 TreeMap。TreeMap 内部使用红黑树数据结构维护键的有序性,它会根据键的自然顺序或者自定义的比较器对键进行排序。

  2. 快速插入与查找:如果你更注重插入和查找操作的速度,而不是键的有序性,那么应该使用 HashMap。HashMap 使用哈希表数据结构,插入和查找操作的平均时间复杂度为 O(1)。

  3. 内存占用:如果你对内存占用有限制,并希望尽量节省内存空间,那么应该考虑使用 HashMap。TreeMap 会维护键的有序性,需要额外的内存空间来存储排序信息,因此相比之下,HashMap 通常占用更少的内存。

  4. 自定义排序需求:如果你希望根据自定义的逻辑对键进行排序,那么应该使用 TreeMap。TreeMap 允许通过自定义比较器来指定键的排序方式,可以实现灵活的排序逻辑。

综上所述,如果你需要按照键的自然顺序来排序,或者需要自定义的排序方式,那么应该使用 TreeMap。如果你更关注插入和查找的速度,并且对键的顺序没有要求,那么应该使用 HashMap。另外,如果你对内存占用有限制,也倾向于使用 HashMap。

🍁🍁 06. ArrayList 和 LinkedList 的区别是什么?

ArrayList 和 LinkedList 是 Java 中常用的两种集合类,它们之间的主要区别包括以下几点:

  1. 底层数据结构:

    • ArrayList 使用数组作为底层数据结构来存储元素,支持动态扩容。
    • LinkedList 使用双向链表作为底层数据结构来存储元素,每个元素都包含对前一个和后一个元素的引用。
  2. 随机访问和插入/删除操作的效率:

    • ArrayList 支持随机访问,即可以通过索引快速获取元素,时间复杂度为 O(1)。但在中间或开头插入/删除元素时,需要将后续元素进行移动,时间复杂度为 O(n)。
    • LinkedList 对于随机访问的效率较低,因为需要从头或尾开始遍历链表以获取指定索引的元素,时间复杂度为 O(n)。但在中间插入/删除元素时,由于只需要修改相邻元素的引用,时间复杂度为 O(1)。
  3. 内存占用:

    • ArrayList 在存储大量元素时会有一定的空间浪费,因为需要预留一定的容量空间,并且在扩容时需要重新分配和复制数组。
    • LinkedList 的每个元素都需要额外的内存空间来存储前一个和后一个元素的引用,因此在存储大量元素时会占用更多的内存空间。
  4. 迭代效率:

    • ArrayList 的迭代效率较高,可以通过索引直接访问元素。
    • LinkedList 的迭代效率较低,因为需要依次遍历链表来获取元素。

综上所述,如果需要频繁进行随机访问操作,应该选择 ArrayList;如果需要频繁进行插入/删除操作,尤其是在集合的中间位置,应该选择 LinkedList。对于内存占用和迭代效率,需要根据具体的场景进行权衡和选择。

以下是一个表格,说明了 ArrayList 和 LinkedList 之间的区别:

特性ArrayListLinkedList
底层数据结构数组双向链表
随机访问效率高效(O(1))低效(O(n))
插入/删除效率低效(O(n))高效(O(1))
内存占用少量额外空间较大额外空间
中间插入/删除效率低效(O(n))高效(O(1))
迭代效率高效低效

该表格总结了 ArrayList 和 LinkedList 之间的主要区别,可以帮助开发人员根据实际需求选择合适的集合类。

🍁🍁 07. 如何实现数组和 List 之间的转换?

要实现数组和 List 之间的转换,可以使用以下方法:

  1. 从数组转换为 List:
    • 使用 Arrays 类的 asList() 方法:可以使用 Arrays.asList() 方法将数组转换为 List。例如:

       int[] array = {1, 2, 3, 4};List<Integer> list = Arrays.asList(array);
      

      注意:通过 asList() 转换得到的 List 是一个固定大小的 List,不支持对其进行添加或删除操作。如果需要对 List 进行修改操作,可以使用其他的 List 实现类(如 ArrayList)进行拷贝。例如:

      List<Integer> list = new ArrayList<>(Arrays.asList(array));
      
  2. 从 List 转换为数组:
    • 使用 toArray() 方法:可以使用 List 的 toArray() 方法将 List 转换为数组。例如:
      List<Integer> list = new ArrayList<>();
      list.add(1);
      list.add(2);
      list.add(3);
      Integer[] array = list.toArray(new Integer[list.size()]);
      

注意:通过 toArray() 方法转换得到的数组是对象数组,如果希望转换为基本类型数组(如 int[]),需要手动进行转换。

需要注意的是,数组和 List 之间的转换只是将数据从一种数据结构转换为另一种数据结构,并不会进行数据的复制。在进行转换时,对其中一个数据结构的修改会影响到另一个数据结构的内容。

🍁🍁 08. ArrayList 和 Vector 的区别是什么?

ArrayList 和 Vector 有以下几点区别:

  1. 线程安全性:

    • ArrayList 不是线程安全的,它的方法没有进行同步处理,因此在多线程环境下使用时需要进行外部同步控制。
    • Vector 是线程安全的,它的方法在内部使用了同步关键字进行同步处理,因此它可以在多线程环境下安全地使用。
  2. 动态增长:

    • ArrayList 和 Vector 都支持动态增长,即当元素数量超过当前容量时,会自动增加容量。
    • ArrayList 的容量增加的策略是当前容量不足时增加原容量的一半,Vector 则是增加当前容量的两倍。
  3. 初始容量的增长:

    • ArrayList 的初始容量为 10,当元素数量超过初始容量时,会自动进行容量增长。
    • Vector 的初始容量为 10,如果需要更大的初始容量,可以通过构造函数指定。
  4. 性能:

    • ArrayList 在单线程环境下的性能会稍微优于 Vector,因为 ArrayList 不需要进行额外的同步处理。
    • Vector 在多线程环境下的性能可能会受到同步的影响稍微低于 ArrayList。

综上所述,ArrayList 和 Vector 最主要的区别在于线程安全性和性能。一般情况下,如果在单线程环境下使用,并且不需要线程安全性保证,可以使用 ArrayList。如果在多线程环境下使用,或者需要线程安全性保证,可以使用 Vector。然而,由于现代 Java 提供了更好的线程安全的集合类(如 CopyOnWriteArrayList),Vector 的使用已经相对较少。

以下是一个表格,说明了 ArrayList 和 Vector 之间的区别:

特性ArrayListVector
线程安全性非线程安全线程安全
动态增长支持动态增长支持动态增长
初始容量增长初始容量为 10,自动增长初始容量为 10,可通过构造函数指定初始容量
性能在单线程环境下性能较好在多线程环境下或需要线程安全性时性能尚可

该表格总结了 ArrayList 和 Vector 之间的主要区别,可以帮助开发人员根据需要选择适合的集合类。

🍁🍁 09. Array 和 ArrayList 有何区别?

Array 和 ArrayList 有以下几点区别:

  1. 大小固定 vs 大小可变:

    • Array(数组)的大小是固定的,在创建数组时需要指定大小,并且在后续操作中无法动态改变其大小。
    • ArrayList(动态数组)的大小是可变的,它可以根据需要自动增长或缩小。
  2. 数据类型:

    • Array 可以存储任何类型的元素,包括基本类型和引用类型。
    • ArrayList 只能存储对象类型(如 Integer、String),不能直接存储基本类型(如 int、char),需要使用对应的包装类。
  3. 内存管理:

    • Array 在内存中是一块连续的存储空间,元素的引用可以直接通过索引进行快速访问和操作。
    • ArrayList 在内存中以动态数组的形式存在,元素在内存中不必连续存储,而是通过索引和引用进行访问和操作。
  4. 自动装箱和拆箱:

    • Array 不支持自动装箱和拆箱,对于基本类型需要手动进行转换。
    • ArrayList 支持自动装箱和拆箱,可以直接存储和操作基本类型的包装类,会自动进行装箱和拆箱的转换。
  5. 功能和灵活性:

    • Array 相对功能较少,只提供了通过索引访问和操作元素的基本功能。
    • ArrayList 提供了丰富的操作方法(如添加、删除、插入、查找等),提供了更高的灵活性和操作便利性。

需要根据具体需求和场景来选择使用 Array 还是 ArrayList。如果大小固定、速度要求较高或直接操作基本类型数据,则可以使用 Array。如果需要动态大小、更多的操作方法和便利性,则可以选择使用 ArrayList。

以下是一个表格,说明了 Array 和 ArrayList 之间的区别:

特性ArrayArrayList
大小固定 vs 大小可变大小固定,无法动态改变大小可变,可以根据需要自动增长或缩小
数据类型可以存储任何类型的元素,包括基本类型和引用类型只能存储对象类型,不能直接存储基本类型,需要使用对应的包装类
内存管理连续的存储空间,通过索引快速访问和操作动态数组形式,通过索引和引用访问和操作
自动装箱和拆箱不支持自动装箱和拆箱支持自动装箱和拆箱
功能和灵活性功能较少,基本的访问和操作功能提供了丰富的操作方法,灵活性和操作便利性较高

该表格总结了 Array 和 ArrayList 之间的主要区别,有助于开发人员根据需求选择适合的数据结构。

🍁🍁 10. 在 Queue 中 poll()和 remove()有什么区别?

在 Queue 接口中,poll() 和 remove() 方法都用于从队列中获取并移除头部元素,但它们在处理空队列时有所不同:

  1. poll() 方法:

    • 当队列为空时,poll() 方法会返回 null 值,表示队列为空,不会抛出异常。
    • 如果队列非空,poll() 方法会移除并返回队列的头部元素。
  2. remove() 方法:

    • 当队列为空时,remove() 方法会抛出 NoSuchElementException 异常,表示队列为空,无法执行移除操作。
    • 如果队列非空,remove() 方法会移除并返回队列的头部元素。

因此,主要区别在于处理空队列时的行为:poll() 方法返回 null,而 remove() 方法抛出异常。在使用时,应根据具体需求考虑如何处理可能出现的空队列情况。

以下是一个表格,说明了 Queue 中 poll() 和 remove() 方法的区别:

特性poll()remove()
空队列时的处理返回 null 值,表示队列为空抛出 NoSuchElementException 异常,表示队列为空
非空队列时的处理移除并返回队列的头部元素移除并返回队列的头部元素
返回值返回 null 或队列头部元素队列头部元素

该表格总结了 Queue 中 poll() 和 remove() 方法在处理空队列和非空队列时的行为差异,以及它们的返回值。开发人员可以根据具体情况选择适合的方法,进行相应的异常处理或空值检查。

🍁🍁 11. 哪些集合类是线程安全的?

在 Java 中,有一些集合类是线程安全的,也就是说它们可以在多线程环境下安全地进行并发操作。一些常见的线程安全的集合类包括:

  1. ConcurrentHashMap:线程安全的哈希表,适合在多线程环境下进行并发读写操作。

  2. CopyOnWriteArrayList:线程安全的列表,采用"写时复制"策略,在写操作时复制一份新的数组,适合读操作远远多于写操作的场景。

  3. CopyOnWriteArraySet:线程安全的集合,基于 CopyOnWriteArrayList 实现,适合读操作远远多于写操作的场景。

  4. ConcurrentLinkedQueue:线程安全的队列,适合在多线程环境下进行并发操作。

  5. ConcurrentSkipListMap 和 ConcurrentSkipListSet:基于跳表实现的线程安全的 Map 和 Set。

需要注意的是,虽然这些集合类是线程安全的,但并不意味着可以完全避免同步和并发问题,仍然需要在具体的业务逻辑中考虑并发访问的情况,使用这些线程安全的集合类并结合合适的同步手段来确保并发操作的安全性。

🍁🍁 12. 迭代器 Iterator 是什么?

迭代器(Iterator)是一种用于遍历(遍历访问)集合(如列表、集合、映射等)元素的对象。它提供了一种统一的方式来遍历不同类型的集合,而不需要了解集合的底层结构。

迭代器通常具有以下几个常见的方法:

  1. hasNext():检查迭代器中是否还有下一个元素。
  2. next():获取迭代器中的下一个元素。
  3. remove():从集合中移除迭代器返回的当前元素(可选操作)。

通过使用迭代器,我们可以以一种统一的方式遍历各种类型的集合元素,无需关心集合是数组、链表还是其他数据结构。它提供了一种简洁、安全且高效的方式来访问和操作集合,同时也避免了直接暴露底层集合的结构。

Java 中的集合框架(如 List、Set、Map 等)都实现了迭代器接口(Iterator),通过调用集合对象的 iterator() 方法可以获取对应的迭代器对象,从而进行迭代操作。

🍁🍁 13. Iterator 怎么使用?有什么特点?

使用 Iterator 进行遍历集合的一般步骤如下:

  1. 通过调用集合对象的 iterator() 方法获取迭代器对象,例如:Iterator<E> iterator = collection.iterator();(这里的 <E> 表示具体的元素类型)。

  2. 使用循环(如 while 或 for)结合 hasNext() 和 next() 方法进行迭代遍历,例如:

    while (iterator.hasNext()) {E element = iterator.next();// 对元素进行操作
    }
    
  3. 可选操作:如果需要在迭代过程中删除集合中的某个元素,可以使用 remove() 方法,例如:iterator.remove();

Iterator 的特点包括:

  1. 统一操作:Iterator 提供了一种统一的方式来遍历不同类型的集合,无需知道集合的具体实现细节,使遍历操作更加简洁、一致且易于使用。
  2. 安全性:通过 Iterator 遍历集合,可以避免在遍历过程中修改集合,以防止并发修改带来的问题。
  3. 只读操作:Iterator 接口中的 remove() 方法允许在迭代过程中删除集合元素,但不允许直接修改集合元素的值。
  4. 快速失败:Iterator 在迭代过程中,如果其他线程对集合进行了结构上的更改(如增加、删除元素),则会抛出 ConcurrentModificationException 异常,以保证在多线程环境下迭代器的安全性。

需要注意的是,Iterator 只能单向遍历,一旦开始遍历,就不能回退或再次遍历,如果需要重新遍历,需要重新获取迭代器对象。

🍁🍁 14. Iterator 和 ListIterator 有什么区别?

Iterator 和 ListIterator 都是 Java 中用于遍历集合的接口,它们有以下区别:

  1. 遍历方向:

    • Iterator:只能向前遍历集合,且只能使用 hasNext() 和 next() 方法。
    • ListIterator:可以双向遍历 List,除了具有 Iterator 的功能外,还可以使用 hasPrevious() 和 previous() 方法向前遍历,以及使用 add()、set() 和 remove() 方法修改集合中的元素。
  2. List 支持:

    • Iterator:可以用于遍历任何类型的集合(如 List、Set、Map),但只能使用基本的遍历能力,无法在遍历过程中修改集合(除非使用 remove())。
    • ListIterator:专门用于遍历 List,可以对 List 进行增删改查的操作,具有更强的功能。
  3. 元素索引:

    • Iterator:没有索引的概念,只能判断是否有下一个元素,并逐个访问元素。
    • ListIterator:除了 hasNext() 和 next() 方法,还可以获得当前访问元素的索引,通过 previous() 和 hasPrevious() 方法可以向前遍历。

总的来说,Iterator 是最基本且通用的集合遍历接口,适用于任何类型的集合,但功能有限。而 ListIterator 是 Iterator 的子接口,只能用于 List 集合,并且提供了更多的遍历和修改功能。因此,在需要对 List 进行双向遍历或修改操作时,可以使用 ListIterator。

以下是 Iterator 和 ListIterator 的区别的简单表格说明:

特性IteratorListIterator
遍历方向单向遍历,只能向前遍历双向遍历,可以向前和向后遍历
支持的集合适用于所有类型的集合(List、Set、Map 等)仅适用于 List 接口
元素索引可以获取当前访问元素的索引,可以向前遍历
读操作hasNext():检查是否有下一个元素hasNext():检查是否有下一个元素
next():获取下一个元素next():获取下一个元素
写操作不支持add():在当前元素之前添加元素
set():替换当前元素的值
remove():删除当前元素
add(E e):在当前元素之后添加元素
并发修改不支持,如果在迭代过程中修改集合,会抛出 ConcurrentModificationException 异常不支持并发修改,如果在迭代过程中修改集合,可能会导致不确定的行为
遍历时删除需要使用 Iterator 的 remove() 方法删除当前元素可以使用 ListIterator 的 remove() 方法删除当前元素
遍历时修改不支持可以使用 ListIterator 的 set() 方法修改当前元素的值

需要根据具体的需求来选择使用 Iterator 还是 ListIterator。如果只需要简单地遍历集合并访问元素,可以使用 Iterator;如果需要对 List 进行双向遍历或修改操作,可以使用 ListIterator。

🍁🍁 15. 怎么确保一个集合不能被修改?

要确保一个集合不能被修改,可以采取以下措施:

  1. 使用不可变集合类:Java 中提供了一些不可变的集合类,如 Collections.unmodifiableXXX() 方法可以生成一个不可修改的集合,例如:

    List<String> originalList = new ArrayList<>();
    List<String> unmodifiableList = Collections.unmodifiableList(originalList);
    

    这样生成的 unmodifiableList 就是一个不可修改的集合了,对其进行修改操作会抛出 UnsupportedOperationException 异常。

  2. 使用只读接口:将集合对象通过只读接口暴露给外部,只在特定的情况下暴露可修改的集合对象。例如,定义一个只读的方法返回集合:

    public List<String> getReadOnlyList() {return Collections.unmodifiableList(internalList);
    }
    
  3. 使用防御性复制:在需要传递集合副本时,不直接传递原始集合对象,而是通过拷贝产生一个新的集合对象进行传递。这样即使外部修改了副本,也不会影响原始集合的数据。例如:

    public List<String> getCopyOfList() {// 防御性复制return new ArrayList<>(originalList);
    }
    

通过以上方法可以较好地确保一个集合不被修改,从而保护数据的完整性。但需要注意的是,使用不可变集合类或只读接口可以在一定程度上保护集合不被修改,但无法防止原始集合内部数据的修改。因此,在设计数据结构时,也需要考虑数据安全性来更好地保护集合不被修改。

🍁🍁 16. 如何实现数组和 List 之间的转换?

要实现数组和 List 之间的转换,可以使用 Java 中的一些内置方法或者手动遍历来进行转换。

从数组转换为 List:

  1. 使用 Arrays 类的 asList() 方法:
String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array);
  1. 手动遍历并添加到 List:
String[] array = {"a", "b", "c"};
List<String> list = new ArrayList<>(array.length);
Collections.addAll(list, array);

从 List 转换为数组:

  1. 使用 List 的 toArray() 方法:
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
String[] array = list.toArray(new String[0]);

通过上述方法,就可以比较方便地实现数组和 List 之间的相互转换。需要注意的是,在进行转换时,需要考虑数组和 List 的元素类型是否匹配,以及转换后数据结构的可操作性。

🍁🍁 17. comparable 和 comparator的区别?

Comparable 和 Comparator 是 Java 中用于比较对象的两种接口,它们的区别如下:

  1. Comparable 接口:
    • java.lang.Comparable 接口是在要比较的类的定义内部实现的,被用来指定该类的自然排序顺序。只能在被比较的对象上实现 Comparable 接口。
    • 实现 Comparable 接口的类在调用 Collections.sort() 方法或 Arrays.sort() 方法时,会自动按照实现 Comparable 接口中的 compareTo() 方法进行排序。
    • compareTo() 方法返回一个整数值,用于描述当前对象和另一个对象的大小关系。如果返回负数,则表示当前对象小于另一个对象;如果返回正数,则表示当前对象大于另一个对象;如果返回 0,则表示两个对象相等。

示例:

public class Person implements Comparable<Person> {private String name;private int age;// 省略其他代码@Overridepublic int compareTo(Person otherPerson) {return this.age - otherPerson.age;}
}
  1. Comparator 接口:
    • java.util.Comparator 接口是在要比较的类的外部单独实现的,用于自定义不同的比较方式。可以为同一个类创建多个不同的比较器,并在需要的时候指定所需的比较器进行排序。
    • 实现 Comparator 接口的类在调用排序方法时,需要手动传入相应的比较器对象。
    • compare() 方法返回一个整数值,用于描述两个对象的大小关系,同样遵循返回负数、正数、0 的规则。

示例:

public class AgeComparator implements Comparator<Person> {@Overridepublic int compare(Person p1, Person p2) {return p1.getAge() - p2.getAge();}
}// 使用方式示例
List<Person> personList = new ArrayList<>();
// ... 添加元素到 personList 中
AgeComparator ageComparator = new AgeComparator();
Collections.sort(personList, ageComparator);

总体来说,Comparable 是在对象自身定义的一种比较方式,而 Comparator 则是针对某个类的外部实现的一种比较方式,因此可以灵活地定义多个不同的比较器来满足不同的排序需求。

区别ComparableComparator
定义位置在对象的类内部实现在对象的类外部单独实现
排序触发当调用 Collections.sort() 或 Arrays.sort() 方法时,会自动按照实现 Comparable 接口中的 compareTo() 方法进行排序需要手动传入相应的 Comparator 对象进行排序
作用用于指定类的自然排序顺序用于自定义不同的比较方式
可扩展性类只能实现一个 Comparable 接口可为同一个类创建多个不同的 Comparator 实现
比较方法compareTo() 方法返回一个整数值,用于描述当前对象和另一个对象的大小关系compare() 方法返回一个整数值,用于描述两个对象的大小关系

以上是 Comparable 和 Comparator 两种比较接口的主要区别。

🍁🍁 18. HashMap是如何解决哈希冲突的?

在Java中,HashMap使用哈希表(Hash Table)作为其底层数据结构来存储键值对。当不同的键被映射到相同的哈希桶(bucket)时,就会发生哈希冲突。HashMap使用了一种开放地址法中的解决冲突的方法,称为"链地址法",具体步骤如下:

  1. 当发生哈希冲突时,HashMap会将具有相同哈希值的键值对存储在同一个哈希桶中,形成一个链表(或者红黑树,后续会解释)。
  2. 每个节点(键值对)都包含键、值和一个指向下一个节点的指针。
  3. 当需要插入或查找一个键值对时,HashMap会计算键的哈希值,然后在对应的哈希桶中搜索键。
  4. 在哈希桶中,HashMap会遍历链表中的节点,通过键的equals()方法与目标键进行比较,找到匹配的键值对。
  5. 如果是插入操作,HashMap会将新的键值对添加到链表的末尾。
  6. 如果是查找操作,HashMap会返回匹配的键值对。

除了链地址法,当链表的长度过长时,为了提高查询效率,HashMap还使用了红黑树(自平衡二叉查找树)优化过程。当链表长度达到一定阈值(默认为8)时,HashMap会将链表转换为红黑树。这样可以将查找操作的时间复杂度从O(n)降低到O(log n)。

总结起来,HashMap解决哈希冲突的方法是在发生冲突时使用链地址法将具有相同哈希值的键值对存储在同一个哈希桶中,并在链表过长时使用红黑树进行优化。这样可以保证HashMap在处理哈希冲突时能够高效地进行插入和查找操作。

🍁🍁 19. 说一下 HashMap 的实现原理?

HashMap 是 Java 中最常用的集合实现之一,它基于哈希表(Hash Table)实现。下面是 HashMap 的实现原理:

  1. 哈希函数:HashMap 使用对象的 hashCode() 方法生成哈希码,通过这个哈希码进行映射。对不同的对象,哈希函数应该尽量保证生成的哈希码分布均匀,避免产生太多的哈希冲突。

  2. 哈希桶数组:HashMap 内部使用一个数组来存储数据,这个数组被称为哈希桶数组。桶数组的初始大小是默认的容量(一般为16),随着元素的增加而自动扩容。

  3. 冲突解决:当不同的键映射到相同的哈希码时,会发生哈希冲突。HashMap使用链地址法来解决冲突。具体而言,相同哈希码的键值对会存储在同一个桶中,并以链表的形式组织起来。

  4. 红黑树优化:当链表的长度超过一定阈值(默认为8),HashMap会将链表转换为红黑树,以提高查找性能。当红黑树的大小降低到一定程度时(默认为6),又会将红黑树转换回链表。

  5. 负载因子与扩容:负载因子指的是当前哈希桶数组已使用桶的占比,当负载因子超过一定阈值(默认为 0.75)时,HashMap会触发扩容操作。扩容会创建一个更大的哈希桶数组,并将原有的键值对重新分配到新的桶中,以减少冲突的可能性。

  6. 桶中的链表或红黑树:每个桶中存储键值对的结构可以是链表,也可以是红黑树。当元素数量较少时,使用链表结构;当元素数量较多时,使用红黑树结构。这样可以保持在不同情况下的高效查找性能。

总结起来,HashMap 的实现原理是使用哈希函数将键映射到哈希码,再根据哈希码在哈希桶数组中存储键值对。当不同键产生哈希冲突时,使用链地址法解决冲突,并通过红黑树优化链表的性能。同时,根据负载因子和扩容机制,保证哈希表的装载因子在一定范围内,以提高效率。

🍁🍁 20. 说一下 HashSet 的实现原理?

HashSet 是 Java 中常用的集合实现之一,它基于哈希表(Hash Table)实现。下面是 HashSet 的实现原理:

  1. 哈希函数:HashSet 使用对象的 hashCode() 方法生成哈希码,通过这个哈希码进行映射。对不同的对象,哈希函数应该尽量保证生成的哈希码分布均匀,提高哈希表的效率。

  2. 哈希桶数组:HashSet 内部使用一个哈希桶数组来存储元素。桶数组的初始大小是默认的容量(一般为16),随着元素的增加而自动扩容。

  3. 冲突解决:当不同的元素映射到相同的哈希码时,会发生哈希冲突。HashSet 使用链地址法(链表或红黑树)来解决冲突。相同哈希码的元素会存储在同一个桶中,并以链表或红黑树的形式组织起来。

  4. 唯一性保证:HashSet 保证集合中的元素是唯一的。当插入一个新元素时,HashSet 会先检查该元素是否已经存在于集合中。通过元素的哈希码和 equals() 方法来判断元素是否重复。

  5. 负载因子与扩容:类似于 HashMap,HashSet 也有负载因子和扩容机制。负载因子表示哈希表已使用桶的占比,当负载因子超过一定阈值(默认为 0.75)时,HashSet 会触发扩容操作。扩容会创建一个更大的哈希桶数组,并将原有的元素重新分配到新的桶中,以减少冲突的可能性。

总结起来,HashSet 的实现原理是通过哈希函数将元素映射到哈希码,并根据哈希码在哈希桶数组中存储元素。当不同元素产生哈希冲突时,使用链地址法解决冲突。通过哈希码和 equals() 方法保证集合中的元素唯一性。同时,根据负载因子和扩容机制,保证哈希表的装载因子在一定范围内,以提高效率。

此外,HashSet 也是基于HashMap实现的,它实际上是通过HashMap的键来实现的。在HashSet中,元素的值被存储为HashMap的键,而所有的value都指向同一个Object对象。这样,在HashSet中插入元素实际上是向HashMap中插入键值对,而值指向同一个Object对象。这也解释了为什么HashSet不允许重复元素,因为HashMap不允许重复的键。因此,HashSet的实现依赖于HashMap的实现。

在具体操作上,向HashSet中插入元素时,程序会首先计算元素的哈希值,然后将其放置在对应的哈希桶中。如果元素已经存在于集合中,插入操作将会失败。这样,在进行查找或插入操作时,HashSet能够通过哈希表快速定位元素,并且在理想情况下具有常数时间的性能。

因此,HashSet的实现原理可以总结为:利用HashMap实现,通过哈希函数将元素映射到哈希桶中,使用HashMap中键唯一的特性来保证集合中元素的唯一性,并通过负载因子和扩容机制来保证集合的性能。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/237806.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SOLIDWORKS Simulation有限元技巧:模拟放置物品时出现的误区

在solidworks simulation中&#xff0c;我们所知simulation能够对零部件和装配体进行有限元分析&#xff0c;而在这种情况下&#xff0c;为了简单分析&#xff0c;同时也为了减少不必要的运算&#xff0c;很多时候我们会选择对整个装配体或是零部件进行简化&#xff0c;如图二&…

免费分享一套Springboot+Vue前后端分离的个人博客系统,挺漂亮的

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringbootVue前后端分离的个人博客系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringbootVue个人博客系统 毕业设计 Java毕业设计_哔哩哔哩_bilibili【免费】SpringbootVue个人博客系统 毕业设计 …

css linear-gradient和radial-gradient的区别

linear-gradient 和 radial-gradient 是 CSS 中用于创建渐变背景的两种主要方法 1.linear-gradient&#xff08;线性渐变&#xff09; linear-gradient 创建沿着一条直线方向的渐变效果。它接受一个角度参数或者两个以上的颜色值作为输入。语法如下&#xff1a; /* 使用角度…

Flink 状态管理与容错机制(CheckPoint SavePoint)的关系

一、什么是状态 无状态计算的例子&#xff1a; 例如一个加法算子&#xff0c;第一次输入235那么以后我多次数据23的时候得到的结果都是5。得出的结论就是&#xff0c;相同的输入都会得到相同的结果&#xff0c;与次数无关。 有状态计算的例子&#xff1a; 访问量的统计&#x…

工业5G路由器提升驾考效率,实现智慧驾考物联网

为了提高驾考的考试效率&#xff0c;更好地服务广大学员&#xff0c;车管所驻考场监控中心结合物联网技术采用智慧驾考系统&#xff0c;实现考场监控、考试员远程监考、学员视频实时回传、自动评判等功能&#xff0c;为驾考公平公正安全提供保障。 该系统由智能监控管理平台和…

深入了解Spring MVC工作流程

目录 1. MVC架构简介 2. Spring MVC的工作流程 2.1. 客户端请求的处理 2.2. 视图解析和渲染 2.3. 响应生成与返回 3. Spring MVC的关键组件 3.1. DispatcherServlet 3.2. HandlerMapping 3.3. Controller 3.4. ViewResolver 4. 结论 Spring MVC&#xff08;Model-Vi…

基于Java版本与鸿鹄企业电子招投标系统的二次开发实践-鸿鹄企业电子招投标系统源代码+支持二开+鸿鹄电子招投标系统

随着市场竞争的加剧和企业规模的扩大&#xff0c;招采管理逐渐成为企业核心竞争力的重要组成部分。为了提高招采工作的效率和质量&#xff0c;我们提出了一种基于电子化平台的解决方案。该方案旨在通过电子化招投标&#xff0c;使得招标采购的质量更高、速度更快&#xff0c;同…

TCP_滑动窗口介绍

简介 TCP协议中有两个窗口&#xff0c;滑动窗口和拥塞窗口&#xff0c;两者均是一种流控机制&#xff1b;滑动窗口是接收方的流控机制&#xff0c;拥塞窗口是发送方的流控机制。 本文介绍滑动窗口&#xff0c;接收方为TCP连接设置了接收缓存。当TCP连接接收到正确、按序的字节…

推荐几个靠谱的视频素材网站,让你的作品更吸引人~

视频素材是视频制作的重要组成部分&#xff0c;它可以提升视频的质量&#xff0c;增加视频的吸引力&#xff0c;让你的视频更容易获得观众的喜爱和关注。如果你想往视频行业发展&#xff0c;或者只是想做一个视频自媒体&#xff0c;那么你一定会遇到一个问题&#xff1a;视频素…

应用在水箱液位检测中的电容传感芯片

水箱水位检测原理通常包括使用传感器来检测水位的变化。常见的传感器类型包括液位开关、液位计和液位传感器。液位开关是一种简单的传感器&#xff0c;它可以检测水位是否达到预定的高度。当水位升高时&#xff0c;开关会打开&#xff1b;当水位降低时&#xff0c;开关会关闭。…

2023年全球架构师峰会(ArchSummit北京站2023)-核心PPT资料下载

一、峰会简介 ArchSummit聚焦业界强大的技术成果&#xff0c;秉承“实践第一、案例为主”的原则&#xff0c;展示先进技术在行业中的典型实践&#xff0c;以及技术在企业转型、发展中的推动作用。旨在帮助技术管理者、CTO、架构师做好技术选型、技术团队组建与管理&#xff0c…

pip 常用指令 pip help 命令用法介绍

&#x1f4d1;pip 常用命令归类整理 pip help 是一个用于获取 Python 包管理器 pip 的帮助信息的命令。它可以帮助我们了解 pip 的各种命令和参数的用法。 pip help 命令的参数主要包括 pip 的各种子命令&#xff0c;例如 install、uninstall、freeze、list 等。你可以使用 p…

MATLAB中var函数用法

目录 语法 说明 示例 矩阵方差 数组方差 指定方差权重向量 指定方差的维度 数组页的方差 排除缺失值的方差 方差和均值 var函数的功能是求取方差。 语法 V var(A) V var(A,w) V var(A,w,"all") V var(A,w,dim) V var(A,w,vecdim) V var(___,nanfla…

2022复盘2023规划(技术篇)

2022复盘&2023规划&#xff08;技术篇&#xff09; 说明&#xff1a; 以下是我对我自身知识掌握度的分级解释 Lv0 简单了解技术点的应用场景 Lv1 前端&#xff1a; 掌握基础知识&#xff0c;能进行简单组件的开发与页面绘制&#xff1b; 后端&#xff1a; 掌握知识点基础&a…

Fabric:使用GoLand+Fabric-SDK-Go操作Fabric网络

遇到bug, 未完待续!!! 写在最前 前序博客已经介绍了使用命令的方式在Fabric上创建通道以及部署执行链码的方法&#xff0c;但这个过程太繁琐&#xff0c;尤其是当Fabric网络中peer节点和组织Org过多时&#xff0c;需要频繁的更改环境变量。 Hyperledger Fabric官方提供了Fabri…

Ubuntu 22.04 LTS上安装Docker-ce

在Ubuntu 22.04 LTS上安装Docker-ce Docker是一个开源平台&#xff0c;用于自动化应用程序的部署、扩展和管理。它使用容器技术&#xff0c;使开发、测试和部署过程更加简化和可靠。本文将介绍在Ubuntu 22.04 LTS上安装Docker-ce的步骤。 步骤1&#xff1a;更新软件包列表 …

5~80V降5V 0.3A同步整流降压型DC-DC转换器-AH7550

AH7550是一种TEL&#xff1a;l86*4884*3702*高压、高效率的同步整流降压型DC-DC开关转换器&#xff0c;恒定120KHz开关频率&#xff0c;输出电流能力高达0.6A&#xff0c;AH7550支持5V~80V的宽输入操作电压范围&#xff0c;同时支持最大占空比90%输出&#xff0c;芯片内置环路补…

小型家用超声波清洗机适合清洗眼镜吗?小型超声波清洗机排行榜

在快节奏的现代生活中&#xff0c;我们的眼睛时常与各种物品接触&#xff0c;如眼镜、隐形眼镜等。为了保持眼睛的健康和舒适&#xff0c;定期清洗眼镜变得尤为重要。而随着科技的发展&#xff0c;小型家用超声波清洗机逐渐进入人们的视野&#xff0c;它能否成为我们清洗眼镜的…

学生备考护眼灯什么牌子好推荐?高性能护眼台灯推荐

作为一名电器测评师&#xff0c;对各类电器好物都了解得比较多&#xff0c;最近也会被很多的人询问护眼台灯哪个牌子好&#xff0c;问其原因才知很多的人有使用护眼台灯出现眼睛酸痛刺眼的现象&#xff0c;这是因为使用了不专业护眼台灯导致的&#xff0c;一般这类护眼台灯使用…

基于springboot的日记本系统源码+数据库+安装使用说明

之前写的SpringBoot日记本系统备受好评&#xff0c;考虑到还是有很多小伙伴不会部署&#xff0c;所以这一篇文章就单独来讲一下部署步骤吧。 需要资源 idea&#xff08;破不破解都行&#xff09; MySQL&#xff08;最好5.7以上版本&#xff0c;最好8.0&#xff09; Navicat…