Java高级应用——集合框架

本文详细介绍了Java集合框架,包括Collection和Map两大体系。Collection体系含List、Set等子接口及对应实现类,如ArrayList、HashSet等;Map体系有HashMap、ConcurrentHashMap等实现类。还介绍了Iterator接口和Collections工具类,阐述各接口和类的特点、方法及使用注意事项。

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

集合框架

Collection和Map两大体系介绍

Java集合可分为Collection和Map两大体系:

  • Collection接口:用于存储一个个的数据,也被称为"单列数据集合"。
    • List子接口:用来存储有序的、可重复的数据,主要用于替代数组,实现了动态数组的功能。
      • 实现类:ArrayList(主要实现类)、LinkedList、Vector。
    • Queue子接口:用于存储一组元素,并支持在集合的一端添加元素、在另一端移除元素。它通常按照特定的规则进行元素的添加和移除,例如先进先出(FIFO)或者优先级排序。
      • 实现类包括LinkedList(实现了双端队列)
      • PriorityQueue(实现了优先级队列)。
    • Deque子接口:是Queue接口的子接口,表示"双端队列"(Double Ended Queue)。它支持在两端进行元素的插入、删除和检索操作。
      • 实现类包括LinkedList(主要实现类)。
    • Set子接口:用来存储无序的、不可重复的数据,类似于高中讲的"集合"。
      • 实现类:HashSet(主要实现类)、LinkedHashSet、TreeSet。
      • EnumSet类:是Set接口的一个特殊实现类,用于存储枚举类型的元素。它基于位向量实现,具有高效的性能和内存利用率。
      • SortedSet接口:是Set接口的子接口,用于存储有序的、不重复的元素。它按照元素的自然顺序或者自定义的比较器进行排序。实现类包括TreeSet(主要实现类)。
      • NavigableSet接口:是SortedSet接口的子接口,提供了一系列用于导航和操作有序集合的方法。它支持根据元素查找、范围查找以及获取最小/最大元素等操作。实现类包括TreeSet(主要实现类)。
  • Map接口:用于存储具有映射关系的"键-值"对集合,即一对一对的数据,也被称为"双列数据集合"。类似于高中的函数、映射关系(例如,(x1, y1),(x2, y2) —> y = f(x))。
    • 实现类:HashMap(主要实现类)、LinkedHashMap、TreeMapHashtableProperties
    • ConcurrentHashMap类:是Map接口的一个实现类,它是线程安全的哈希表实现,支持高并发的读写操作。相比于HashMap,它在并发环境下提供了更好的性能和线程安全性。
    • SortedMap接口:是Map接口的子接口,用于存储有序的键值对。它按照键的自然顺序或者自定义的比较器进行排序。实现类包括TreeMap(主要实现类)。
    • NavigableMap接口:是SortedMap接口的子接口,提供了一系列用于导航和操作有序映射的方法。它支持根据键查找、范围查找以及获取最小/最大键等操作。实现类包括TreeMap(主要实现类)。

Iterator(迭代器)接口

  • Iterator接口:用于遍历集合中的元素,并提供了一种统一的方式来访问集合中的元素,而不需要了解底层的数据结构。它定义了一些方法,如hasNext()next()remove(),用于遍历和操作集合中的元素。

方法:

  1. boolean hasNext(): 返回一个布尔值,表示集合中是否还有下一个元素可以遍历。如果有,则返回true;否则返回false。
  2. E next(): 返回集合中的下一个元素,并将迭代器的指针向后移动。如果没有下一个元素可遍历,将抛出NoSuchElementException异常。
  3. void remove(): 从集合中移除通过迭代器最后一次返回的元素(可选操作)。该方法只能调用一次,每次调用必须在调用next()之后进行。如果在调用next()之前或者已经调用了remove(),再次调用remove()将会抛出IllegalStateException异常。

注意事项:

  • 迭代器在遍历过程中,可以使用remove()方法从集合中移除元素。但如果在调用next()之前或者连续两次调用remove(),将会抛出IllegalStateException异常。
  • 如果在迭代器的遍历过程中,通过集合的其他方式(例如直接调用集合的remove()方法)修改了集合的结构,迭代器将变为不合法状态,继续使用迭代器的方法可能会导致未定义的行为。
  • 迭代器是通过集合的iterator()方法获取的,例如Iterator<E> iterator() 。每次调用iterator()方法都会返回一个新的迭代器实例,它的初始位置位于集合的第一个元素之前。
  • 迭代器是设计用于单向遍历的,不支持逆向遍历。如果需要逆向遍历集合,可以考虑使用ListIterator接口。

Collection——List接口

特点:

  1. 有序性:List中的元素按照插入的顺序进行存储,并且可以根据索引访问和操作元素。这意味着元素在List中的位置是可以确定的。
  2. 可重复性:List允许存储重复的元素,即可以包含多个相同的元素。
  3. 索引访问:通过索引可以直接访问List中的元素。索引的范围是从0到size()-1,其中size()方法返回List中的元素个数。
  4. 可变性:List是可变的,可以根据需要插入、删除和修改元素。可以使用add()方法在指定位置插入元素,使用remove()方法删除指定位置的元素,使用set()方法修改指定位置的元素。
  5. 可以使用foreach循环进行遍历:List可以通过foreach循环遍历其中的元素,也可以使用迭代器(Iterator)进行遍历。

常用方法:

  1. boolean add(E element): 将指定的元素添加到List的尾部。
  2. void add(int index, E element): 在指定的位置插入指定的元素。
  3. boolean remove(Object element): 从List中删除指定的元素,如果存在多个相同的元素,只删除第一个匹配的元素。
  4. E remove(int index): 删除指定位置的元素,并将其返回。
  5. E get(int index): 返回指定位置的元素。
  6. E set(int index, E element): 修改指定位置的元素,并返回被替换的元素。
  7. int indexOf(Object element): 返回指定元素在List中第一次出现的索引,如果不存在则返回-1。
  8. int size(): 返回List中的元素个数。
  9. boolean isEmpty(): 判断List是否为空,如果为空则返回true,否则返回false。

注意事项:

  • List接口是一个接口,不能直接实例化,需要使用它的实现类如ArrayList、LinkedList等进行实例化。
  • List接口中的索引从0开始,因此访问第一个元素的索引是0,访问最后一个元素的索引是size()-1。
  • List接口允许存储null元素。
  • List接口继承自Collection接口,因此也继承了Collection接口中的一些方法,如boolean contains(Object element)void clear()等。
  • List接口可以通过迭代器(Iterator)进行遍历,也可以使用foreach循环遍历。
List接口的实现类
ArrayList类:
  • 特点:基于动态数组实现,内部使用数组来存储元素,支持快速随机访问和修改,适用于随机访问和频繁修改元素的场景。
  • 优点:读取、修改元素的时间复杂度为O(1),插入、删除元素的时间复杂度为O(n)。
  • 缺点:在插入和删除元素时,需要移动其他元素的位置,性能较LinkedList差。
LinkedList类:
  • 特点:基于双向链表实现,内部使用链表来存储元素,支持快速插入和删除操作,适用于频繁插入和删除元素的场景。
  • 优点:插入、删除元素的时间复杂度为O(1),读取、修改元素的时间复杂度为O(n)。
  • 缺点:随机访问元素的性能较ArrayList差。
Vector类:
  • 特点:与ArrayList类似,基于动态数组实现,是ArrayList的线程安全版本,支持快速随机访问和修改。
  • 优点:读取、修改元素的时间复杂度为O(1),插入、删除元素的时间复杂度为O(n)。
  • 缺点:线程安全的开销较大,在单线程环境下性能较ArrayList差。
Stack类:
  • 特点:继承自Vector类,表示堆栈(后进先出)数据结构,支持将元素压栈和弹栈操作。

Collection——Set接口

特点:

  1. 不允许重复元素:Set接口中不允许包含重复的元素。如果试图将重复元素添加到Set中,添加操作将被忽略,不会抛出异常。
  2. 无序性:Set接口不保证元素的顺序,即元素在Set中的存储顺序与添加顺序可能不同。具体的实现类可能会根据自身的规则对元素进行排序。
  3. 基于哈希表或树结构实现:Set接口的具体实现类可以基于哈希表(如HashSet、LinkedHashSet)或树结构(如TreeSet)来存储元素。
  4. 哈希表实现:
    • HashSet类:基于哈希表实现,使用哈希函数来存储元素,具有较快的添加、删除和查找操作的性能。
    • LinkedHashSet类:基于哈希表和链表实现,除了具有HashSet的特点外,还保持了元素的插入顺序。
  5. 树结构实现:
    • TreeSet类:基于红黑树(自平衡二叉查找树)实现,元素按照特定的顺序进行存储,可以提供有序的集合视图。
  6. 元素的判定依据:Set接口使用元素的equals()和hashCode()方法来判断元素的重复性。因此,对于自定义对象,需要正确实现equals()和hashCode()方法以确保正确的去重和查找。
  7. 支持null元素:Set接口允许存储一个null元素(某些实现类除外),但只能存储一个null元素,不能存储多个null元素。
Set接口的实现类:
HashSet类
  • 特点:

    • HashSet基于哈希表实现,利用哈希函数存储元素,不保证元素的顺序,具有快速的添加、删除和查找操作性能。
    • 插入、删除和查找元素的时间复杂度为O(1)。
    • HashSet不保证元素的顺序,不适用于需要有序集合的场景。
  • HashSet的其他特点包括:

    • 不能保证元素的排列顺序。
    • HashSet不是线程安全的。
    • 集合元素可以为null。
  • HashSet判断两个元素相等的标准是:

    • 通过hashCode()方法获取的哈希值相等。
    • 通过equals()方法比较返回true。
  • HashSet是Set接口的主要实现类,通常在使用Set集合时使用HashSet。

  • HashSet按照哈希算法存储集合中的元素,因此具有良好的存储、查找和删除性能。

  • HashSet集合中元素的无序性不等同于随机性。无序性与元素的添加位置有关,元素在数组中的存储位置由元素的hashCode()方法返回的哈希值决定,导致数组中的元素并非连续存放,呈现一定的无序性。


向HashSet集合中添加元素的过程包括

  1. 当向HashSet集合存入元素时,HashSet会调用该对象的hashCode()方法获取哈希值,并根据哈希值通过散列函数确定该对象在底层数组中的存储位置。
  2. 如果存储位置上没有元素,则直接添加成功。
  3. 如果存储位置上已经有元素,则继续比较:
    • 如果两个元素的hashCode值不相等,则添加成功。
    • 如果两个元素的hashCode值相等,则继续调用equals()方法:
      • 如果equals()方法返回false,则添加成功。
      • 如果equals()方法返回true,则添加失败。

当程序运行时,同一个对象多次调用hashCode()方法应返回相同的值。当两个对象的equals()方法返回true时,它们的hashCode()方法的返回值也应相等。对象中用于equals()方法比较的字段应参与计算hashCode值。


在重写equals()方法时,通常需要同时重写hashCode()方法。推荐使用Eclipse/IDEA等开发工具提供的快捷键自动重写equals()和hashCode()方法。

为什么使用31作为hashCode计算中的系数?

选择较大的系数可以减少冲突,提高查找效率。31只占用5个bits,相乘造成数据溢出的概率较小。31可以表示为i*31=(i<<5)-1,这种表示在现代虚拟机中进行优化。此外,31是一个素数,因为用一个数字乘以素数,结果只能被素数本身、乘数和1整除,有助于减少冲突。

LinkedHashSet类:
  • 特点:

    • LinkedHashSet基于哈希表和链表实现,继承了HashSet的特点,并保持了元素的插入顺序。
    • 元素的插入顺序在集合中得以保持,这是通过使用双向链表来维护元素次序实现的。

    优点:

    • 在LinkedHashSet中,插入、删除和查找元素的时间复杂度均为O(1)。这意味着这些操作具有很高的效率。
    • LinkedHashSet不允许集合中存在重复的元素。每个元素在集合中都是唯一的。
  • LinkedHashSet 是 HashSet 的子类,不允许集合元素重复

  • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以添加顺序保存的。

  • LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。

TreeSet类:
  • 特点:
    • 基于红黑树(自平衡二叉查找树)实现,元素按照特定的顺序进行存储,提供有序的集合视图。
    • TreeSet是SortedSet接口的实现类,可以按照添加的元素的指定属性的大小顺序进行遍历。
  • 优点:
    • 素按照自然排序或指定的比较器进行排序,支持快速的插入、删除和查找操作。
  • 缺点:
    • 插入、删除、查找元素的时间复杂度为O(log n),因为涉及红黑树的平衡调整操作。
  • TreeSet底层使用红黑树结构存储数据。
  • TreeSet不允许重复元素,并且实现了排序功能(自然排序或定制排序)
  • TreeSet提供了两种排序方法:自然排序和定制排序。默认情况下,TreeSet采用自然排序。
    • 自然排序:TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序(默认情况)排列
      • 如果试图将一个对象添加到TreeSet时,该对象的类必须实现Comparable接口。
      • 实现Comparable的类必须实现compareTo(Object obj)方法,通过该方法的返回值来比较对象的大小。
    • 定制排序:如果元素所属的类没有实现Comparable接口,或者不希望按照升序(默认情况)的方式排列元素,或者希望按照其他属性的大小进行排序,则可以考虑使用定制排序。定制排序通过Comparator接口实现,需要重写compare(T o1, T o2)方法。
      • 利用compare(T o1, T o2)方法比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
      • 要实现定制排序,需要将实现Comparator接口的实例作为参数传递给TreeSet的构造器。
  • 向TreeSet中添加元素时,由于只有相同类的两个实例才会比较大小,因此应该添加同一个类的对象。
  • 对于TreeSet集合而言,判断两个对象是否相等的唯一标准是通过compareTo(Object obj)方法或compare(Object o1, Object o2)方法的返回值来比较。如果返回值为0,则认为两个对象相等。
EnumSet类:
  • 特点:专门用于存储枚举类型的集合,内部使用位向量实现,具有非常高的性能和内存效率。
  • 优点:支持高效的按位操作,只能存储同一枚举类型的元素。

Map接口

Java中的Map接口表示一种键值对的映射关系

Map接口的特点:
  1. Map接口用于存储键值对,其中每个键都是唯一的
  2. Map接口不允许键重复,但允许值重复
  3. Map中的键值对没有固定的顺序,可以根据需要进行排序
  4. Map接口允许使用null键和null值
Map接口的主要方法:

Map信息查询方法:

  1. int size(): 返回Map中键值对的数量。
  2. boolean isEmpty(): 判断Map是否为空。

Map元素判断方法:

  1. boolean containsKey(Object key): 判断Map中是否包含指定的键。
  2. boolean containsValue(Object value): 判断Map中是否包含指定的值。

Map元素获取方法:

  1. V get(Object key): 根据键获取对应的值,若键不存在则返回null。

Map元素添加和修改方法:

  1. V put(K key, V value): 将指定的键值对添加到Map中,并返回之前与该键关联的值(如果存在)。

Map元素删除方法:

  1. V remove(Object key): 根据键删除对应的键值对,并返回该键关联的值。

Map批量操作方法:

  1. void putAll(Map<? extends K, ? extends V> m): 将指定的Map中的所有键值对添加到当前Map中。

Map清空操作方法:

  1. void clear(): 清空Map中的所有键值对。

Map元素遍历方法:

  1. Set<K> keySet(): 返回Map中所有键的集合。
  2. Collection<V> values(): 返回Map中所有值的集合。
  3. Set<Map.Entry<K, V>> entrySet(): 返回Map中所有键值对的集合。
Map接口的实现类

Map接口有多个实现类,其中常见的包括:

  • HashMap:基于哈希表实现,不保证元素的顺序。
  • LinkedHashMap:基于哈希表和链表实现,保持元素的插入顺序。
  • TreeMap:基于红黑树实现,按照键的自然顺序或自定义比较器进行排序。
  • Hashtable:线程安全的实现类,与HashMap类似,但支持同步操作。
  • ConcurrentHashMap:线程安全的实现类,支持高并发环境下的操作。

在使用Map接口时,可以根据具体的需求选择合适的实现类。需要注意的是,对于无序性要求较高的情况,可以选择HashMap或LinkedHashMap;对于有序性要求较高的情况,可以选择TreeMap。在多线程环境下,需要考虑使用线程安全的实现类Hashtable或ConcurrentHashMap。

此外,Map接口的键对象需要正确实现equals()hashCode()方法,以确保正确的键值对查找和存储操作。通常情况下,使用具有良好散列分布的键对象可以提高性能。

HashMap类

特点:

  1. HashMap类使用哈希表实现,通过键的哈希码来确定键值对的存储位置
  2. HashMap类不保证元素的顺序,即元素的顺序可能不同于插入的顺序。
  3. HashMap允许使用null作为键和值
  4. HashMap类不是线程安全的,如果在多线程环境下使用,需要进行额外的同步处理。
  5. HashMap 判断两个key相等的标准是:两个 key 的hashCode值相等,通过 equals() 方法返回 true。
  6. HashMap 判断两个value相等的标准是:两个 value 通过 equals() 方法返回 true。

性能:

  1. 在HashMap中,插入、删除和查找元素的时间复杂度通常为O(1),具有很高的效率。
  2. HashMap的性能与负载因子(load factor)有关。负载因子是指HashMap在自动扩容之前可以达到的填充比例。默认负载因子为0.75,可以在构造HashMap时指定。
  3. 当HashMap中的元素数量接近负载因子与容量的乘积时,将会自动扩容,以保持较低的填充比例,提高性能。
ConcurrentHashMap类

特点:

  1. ConcurrentHashMap类是线程安全的,可以在多线程环境下进行并发访问和修改。
  2. ConcurrentHashMap类采用分段锁(Segment)的机制来实现线程安全,不同的段可以被不同的线程同时访问,提高了并发性能。
  3. ConcurrentHashMap类不保证元素的顺序,即元素的顺序可能与插入的顺序不同。

性能:

  1. ConcurrentHashMap类在多线程环境下提供高效的并发操作。
  2. ConcurrentHashMap类将数据分割成多个段(Segment),每个段都有自己的锁,不同的线程可以同时访问不同的段,从而提高了并发性能。
  3. ConcurrentHashMap类的性能与并发级别有关。可以在构造ConcurrentHashMap时指定并发级别,默认为16。并发级别影响了ConcurrentHashMap内部的段(Segment)数量,可以根据并发访问的线程数量进行调整。
LinkedHashMap类

特点:

  1. LinkedHashMap类继承自HashMap类,具有HashMap的特性,如高效的查找和插入操作。
  2. LinkedHashMap类通过双向链表来维护元素的插入顺序,可以保持元素的迭代顺序与插入顺序一致。
  3. LinkedHashMap类不保证元素的顺序与键的自然顺序或自定义排序顺序一致。

迭代顺序:

  1. LinkedHashMap类可以按照插入顺序进行迭代,即迭代顺序与元素的插入顺序一致。
  2. 如果使用构造函数LinkedHashMap(int initialCapacity)创建LinkedHashMap,迭代顺序将与插入顺序一致。
  3. 如果使用构造函数LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)创建LinkedHashMap,并将accessOrder参数设置为true,迭代顺序将根据访问顺序进行调整,即最近访问的元素将排在最后。
TreeMap类

特点:

  1. TreeMap类是基于红黑树实现的,它可以保持键的有序性。
  2. TreeMap中的键必须实现Comparable接口或者在构造TreeMap时提供Comparator比较器来定义键的顺序。
  3. TreeMap类不允许使用null作为键,但允许使用null作为值。

性能:

  1. 在TreeMap中,插入、删除和查找元素的时间复杂度为O(logN),其中N为TreeMap中元素的数量。
  2. TreeMap根据键的顺序进行存储和遍历,所以适用于需要按照键的顺序进行操作的场景。
  3. 当需要对键进行排序或按范围进行检索时,TreeMap是一个很好的选择。
Hashtable类

特点:

  1. Hashtable**是线程安全的,即多线程环境下的操作是同步的。**可以通过在多个线程之间共享Hashtable对象来实现线程安全的操作。
  2. Hashtable不**允许使用null作为键或值。**如果尝试插入null键或值,会抛出NullPointerException异常。
  3. Hashtable使用哈希表来存储键值对,通过键的哈希值进行快速查找和插入操作。

性能:

  1. Hashtable中的插入、删除和查找操作的时间复杂度为O(1),在大多数情况下是常数时间。
  2. 由于Hashtable在多线程环境下是线程安全的,因此在并发访问场景下,性能可能受到一定的影响。如果不需要线程安全性,使用Hashtable可能会带来额外的开销。在单线程环境下,推荐使用HashMap来获得更好的性能。
Properties类

特点:

  1. Properties类用于处理属性文件,属性文件是一种简单的文本文件,包含以键值对形式表示的配置信息。
  2. Properties类继承自Hashtable类,因此具有Hashtable的特性,如使用哈希表存储键值对、支持快速查找和插入操作。
  3. Properties类对外提供了一些特殊的方法,以方便加载和存储属性文件的操作。

作用:

可以通过getProperty方法获取特定键对应的值。同时,Properties类也支持将属性数据以XML格式存储和加载,以满足不同的需求。

需要注意的是,Properties类是线程安全的,可以在多线程环境下使用。但在多线程环境下,如果同时对属性文件进行读写操作,可能需要额外的同步措施来确保数据的一致性。

Collection的工具类Collections类

Collections类:
  • Collections类是Java集合框架提供的一个实用类,包含了一系列静态方法,用于操作和处理集合对象。
  • Collections类提供了各种算法和方法,用于对集合进行排序、查找、反转、替换等操作。
  • Collections类中的方法都是静态方法,可以直接通过类名调用,而不需要创建实例。
  • Collections类还提供了一些方法来创建线程安全的集合,如synchronizedCollection()synchronizedList()等。

总结:

  • Collection是一个接口,定义了集合类的通用操作和行为。
  • Collections是一个实用类,提供了各种静态方法,用于操作和处理集合对象。
  • Collection接口和Collections类是Java集合框架中的重要组成部分,用于处理和操作集合数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值