重学 Java 第20天 Java 常用集合类

225 阅读17分钟

Java 常用集合类

集合类描述

Iterator 与 Enumeration

Iterator 用于进行数据迭代,叫做迭代器;Enumeration 用于进行数据枚举,称做枚举

  1. 枚举(Enumeration) 接口

    // boolean hasMoreElements();
    // Object nextElement();
    
    Enumeration e = v.sogou.comelements();
    while(e.hasMoreElements()) {
      System.out.println(e.nextElement());
    }
    
  2. 迭代器(Iterator) 接口

    // boolean hasNext();		 如果仍有元素可以迭代,则返回 true
    // E next();						 返回迭代的下一个元素
    // void remove();				 从迭代器指向Collection 中移除迭代器返回的最后一个元素
    
    Iterator it = vect.iterator();
    while(it.hasNext()){
      Object obj = item.next();
    }
    

    区别:

    • Enumeration 枚举接口其实是一个比Iterator 迭代器接口更早期的枚举集合中的元素的接口
    • Enumeration 虽然可能过时而被Iterator 替代,但很多Servlet 还用到,所以还有学习的必要
    • 迭代器允许调用方利用定义良好的语义在迭代期间从迭代器指向的集合移除元素
    • 方法名称得到了改进

Collections 与 Collection

  • Collection 是个 java.util 下的接口,他提供了对集合对象进行基本操作的通用接口方法。Collection接口在 Java类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式
  • Collections 是个java.util 下的实体类,它包含有各种有关集合操作的静态方法,提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作,就像一个工具类,服务于 Java的 Collection 框架
  1. Collection

    • 新增一个或多个元素

      boolean add(E e);
      boolean addAll(Collection<? extents E> e);
      
    • 删除一个或多个元素

      boolean remove(Object o);
      boolean removeAll(Collection<? extents E> e);
      void clear();
      boolean isEmpty();
      int size();
      
    • 判断是否包含一个或多个元素

      boolean contains(Object o);
      boolean containsAll(Collection<? extents E> e);
      
    • 产生元素集合的数组对象

      Iterator<E> iterator();
      Object[] toArray();
      <T> T[]
      toArray(T[] a);
      
  2. Collections

    • 新增

      boolean addAll(Collection<? extents E> e);
      
    • 填充

      void fill(List<? super T> list, T ob);
      
    • 复制

      void copy((List<? super T> dest, (List<? extends T> src);
      
    • 替换

      boolean replaceAll(List<? super T> list, T oldVal, T newVal);
      
    • 排序

      void sort(List<T> list);
      void sort(List<T> list, Comparator<? super T> e);
      
    • 搜索

      int binarySearch(List <? extends Comparable<? super T>> list, T key);
      int binarySearch(List <? extends T> list, T key, Comparator<? super T> c);
      
    • 查找位置

      int indexOfSubList(List<?> source, List<?> target);
      int lastIndexOfSubList(List<?> source, List<?> target);
      
    • 求最大或最新元素

      max(Collection<? extends T> coll);
      min(Collection<? extends T> coll);
      

Arrays 与 数组

  • 填入——fill()
  • 复制——copyOf()
  • 比较——equals()
  • 搜索——binarySearch()
  • 排序——sort()

Dictionary 字典

​ Dictionary 就是我们通常所说的字典类,它是任何可将键映射到相应值的类的抽象父类。每个键和每个值都是一个对象。在任何一个Dictionary对象中,每个键最多与一个值相关联。给定一个Dictionary和一个键就可以查找所关联的元素。任何非null对象都可以用做键或值

枚举:abstract Enumeration<V> elements();
取值:abstract V get(Object key);
判断为空:abstract boolean isEmpty();
键枚举:abstract Enumeration<K> keys();
设值:abstract V put(K key, V value);
删除:abstract V remove(Object key);
取得大小:abstract int size();

Queue[kjuː] 队列

  1. Queue 队列接口

    Queue 除了基本的Collection[kəˈlekʃn]操作外,还提供了其他的插入、提取和检查操作。每个方法都存在两种形式:一种抛出异常(操作失败时),另一种返回一个特殊值(null或false,具体取决于操作)。

    操作抛出异常返回特殊值描述
    插入add(e)offer(e)将指定的元素插入此队列
    获取移除remove()poll()获取并移除队列的头
    获取不移除element()peek()获取不移除此队列的头

    队列通常以FIFO(先进先出)的方式排序各个元素

    优先队列:根据提供的比较器或元素的自然顺序对元素进行排序

    LIFO队列:按LIFO(后进先出)的方式元素进行排序

    无论使用哪种排序方式,队列的头都是调用 remove()或poll()所移除的元素。在FIFO队列中,所有的新元素都插入队列的末尾。其他种类的队列可能使用不同的元素放置规则。每个Queue实现必须指定其顺序属性

  2. AbstractQueue 抽象类

    此类提供某些Queue操作的抽象实现。在此类中的实现适用于基本实现不允许包含null元素时。add()、remove()和element()方法分别基于offer()、pull()和peek()方法,但是它们通过抛出异常而不是返回false 或 null来指示失败

  3. 优先级 PriorityQueue

    一个基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的Comparator进行排序,具体取决于所使用的构造方法。优先级队列不允许使用 null 元素。依靠自然顺序的优先级队列还不允许插入不可比较的对象。

    此队列的头是按指定排序方式确定的最小元素。如果多个元素都是最小值,则头是其中一个元素。

    优先级队列是无界的,但是有一个内存容量,控制着用于存储队列元素的数组大小。它通常至少等于队列的大小。随着不断向优先级队列添加元素,其容量会自动增加。无须指定容量增加策略的细节。

列表类 List

抽象类 AbstractList 与 AbstractSequentialList

AbstractList 提供 List 接口的抽象实现,以最大限度地减少实现 “随机访问” 数据存储支持的该接口所需的工作。对于连续的访问数据,应优先使用AbstractSequentialList,而不是此类

在继承AbstractList 时:

  • 如果要实现不可修改的列表,只需扩展此类,并提供get(int)和size()方法的实现

  • 要实现可修改的列表,必须另外重写set(int, E)方法

  • 如果列表为可变大小,则必须另外重写add(int, E)和 remove(int)方法

    目前继承自AbstractList的实现类有ArrayList 和 Vector

链表 LinkedList

​ LinkedList 是 List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括null)。除了实现List接口外,LinkedList 类还在为列表的开头及结尾 get()、remove()和add()元素提供了统一的命名方法。这些操作允许将链接列表用做堆栈、队列或双端队列

void addFirst(E e);			// 将指定元素插入此列表的元素
void addLast(E e);			// 将指定元素添加到此列表的结尾

boolean offerFirst(E e);			// 在此列表的开头插入指定的元素
boolean offerLast(E e);			// 在此列表末尾插入指定的元素

E getFirst();					// 返回此列表的第一个元素
E getLast();					// 返回此列表的最后一个元素

E removeFirst();					// 移除并返回此列表的第一个元素
E removeFirst();					// 移除并返回此列表的最后一个元素

E peekFirst();					// 获取但不移除此列表的第一个元素;如果列表为空,则返回 null
E peekLast();					// 获取但不移除此列表的最后一个元素

E pollFirst();					// 获取并移除此列表的第一个元素
E pollLast();					// 获取并移除此列表的最后一个元素

可变数组 ArrayList

ArrayList 是 List 接口的大小可变数组的实现。实现了所有可选列表的操作,并允许包括null 在内的所有元素。除了实现List接口外,此类还提供一些方法来操作用来存储列表的数组的大小

每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小,它总是至少等于列表的大小。

向量 Vector[ˈvektər]

Vector类可以实现可增长的对象数组。与数组一样,它包含可以使用证书索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建Vector后进行添加或移除项的操作。

  • addElement(Object obj);:将组件添加到向量尾部,同时大小加1,向量容量比以前大1.
  • insertElementAt(Object obj, int index);:把组件添加到所指定的索引处,此后的内容向后移动1个单位
  • setElement(Object obj, int index);:把组件添加到所指定的索引处,此处的内容被代替。
  • removeElement(Object obj);:把向量中含有本组件内容移走
  • removeAllElements();:把向量中所有组件移走,向量大小为0

堆栈 Stack[stæk]

​ Stack 类表示后进先出(LIFO)的对象堆栈。它通过5个操作对类Vector进行了扩展,运行将向量视为堆栈。它提供了通常的push()和pop()操作,以及取堆栈顶点的peek()方法、测试堆栈是否为空 empty()方法、在堆栈中查找项并确定到堆栈顶距离的search()方法

集合类 Set

  • HashSet 能快速定位一个元素,但是你放到HashSet中的对象需要HashCode()方法,它使用了前面说过的哈希码的算法
  • TreeSet 则将方法其中的元素按序存放,这就要求你放入其中的对象是可排序的,这就用到了集合框架提供的另外两个实体类 Comparable 和 Comparator 。一个类是可排序的,它就应该实现Comparable 接口。有时多个类具有相同的排序算法,那就不需要分别在每个类中重复定义相同的排序算法,只要实现Comparator接口即可。集合框架中还有两个很实用的公用类:Collections 和 Arrays。

抽象类 AbstractSet 与 接口 SortedSet

Set 有一个抽象类和接口,分别是AbstractSet 和 SortedSet。AbstractSet 为实现哈希做准备,SortedSet 为实现具有排序功能的 TreeSet 做准备

  1. 哈希抽象类 AbstractSet

    AbstractSet 通过扩展此类来实现一个Set的过程与通过扩展AbstractCollection 来实现 Collection的过程是相同的,除了此类的子类中的所有方法和构造方法都必须服从Set 接口所强加的额外限制

  2. 排序接口 SortedSet

    SortedSet 进一步提供关于元素的总体排序的Set。这些元素使用其自然顺序进行排序,或者根据在创建有序Set时提供的Comparator进行排序。所有这些元素都必须是可互相比较的。所有通用有序Set实现类都应该提供4个标准构造方法

    • void(无参数)构造方法:它创建一个空的有序Set,按照元素的自然顺序进行排序
    • 带有一个Comparator类型参数的构造方法:它创建一个新的有序Set,其元素与参数相同,按照元素的自然顺序进行排序
    • 带有一个SortedSet类型参数的构造方法:它创建一个新的有序Set,其元素和排序方法与输入的有序Set相同

    TreeSet 就是一个排序接口 SortedSet 的实现

哈希集合 HashSet

HashSet 由哈希表(实际上是一个HashMap 实例)支持,为基本操作提供了稳定性能,这些基本操作包括 add()、remove()、contains()和size(),假定哈希函数将这些元素正确地分布在桶中。对此 set()进行迭代所需的时间与HashSet实例的大小和底层HashMap的容量的和成比例,因此,如果迭代性能很重要,则不要将初始容量设置得太高

树集合 TreeSet

TreeSet 是基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建Set时提供的 Comparator 进行排序,具体取决于使用构造方法。此实现为基本操作提供受保证的log时间开销。

映射类 Map

Map 是一种把键对象和值对象进行关联的容器,而一个值对象又可以是一个Map,依次类推,这样就可形成一个多级映射。对于键对象来说,像Set一样,一个Map容器中的键对象不允许重复,这是为了保持查找结果的一致性。

  • HashMap 也用到了哈希码的算法,以便快速查找一个键
  • TreeMap 是对键按序存放,因此它便有一些扩展的方法

抽象类 AbstractMap 与 接口 SortedMap、NavigableMap

Map 有一个抽象类和两个接口,分别是AbstractMap、SortedMap和Navigable,NavigableMap又是SortedMap的子接口。AbstractSet 为实现哈希做准备,SortedMap 为实现具有排序功能的TreeMap做准备

  1. 哈希抽象类 AbstractMap

    • 保存键值—V put(K key, V value);:将指定的值与此映射中的指定键关联
    • 删除键值—V remove(Object key);:如果存在一个键的映射关系,则将其从此映射中移除
    • 取得键值—V get(Object key);:返回指定键所映射的值;如果此映射不包含。该键的映射关系,则返回 null
    • 取得键集合—Set<K>keySet();:返回此映射中包含的键的Set视图

    要实现不可修改的映射,只需扩展此类并提供entrySet()方法的实现即可,该方法将返回映射的映射关系Set视图。通常的 set()将依次在AbstractSet上实现。此set()不支持add()或remove()方法

    要实现可修改的映射,必须另外重写此类的 put() 方法,entrySet().iterator()返回的迭代器也必须另外实现其 remove()方法

  2. 排序接口 SortedMap

    该接口进一步提供关于键的总体排序的Map。该映射是根据其键的自然顺序进行排序的,或者根据通常在创建有序映射时提供的Comparator进行排序

    K firstKey(); // 返回此映射是根据其键的自然顺序进行排序的,或者根据通常
    K lastKey();
    

    插入有序映射的所有键都必须实现Comparable 接口。

    • void(无参数)构造方法:它创建一个空的有序映射,按照键的自然顺序进行排序
    • 带有一个Comparator类型参数的构造方法:它创建一个空的有序映射,根据指定的比较器进行牌组
    • 带有一个Map类型参数的构造方法:它创建一个新的有序映射,其键一值映射关系与参数相同,安装键的自然顺序进行排序
    • 带有一个SortedMap类型参数的构造方法:它创建一个新的有序映射,其一键一值映射关系和排序方法与输入的有序映射相同。无法保证强制实施此建议,因为接口不能包含构造方法
  3. 导航接口 NavigableMap

    该接口扩展自 SortedMap,具有针对给定搜索目标返回最接近匹配项的导航方法。方法lowerEntry()、floorEntry()和higherEntry()分别小于、小于等于、大于等于、大于给定键的键关联的Map.Entry对象,如果不存在这样的键,则返回null。

    此接口还定义了 firstEntry()、pollFirstEntry()、lastEntry()和pollLastEntry()方法,他们返回和/或移除最小和最大的映射关系,否则返回null

树映射 TreeMap

​ TreeMap即继承了哈希抽象类AbstractMap,又实现排序接口SortedMap和导航接口NavigableMap。它是基于红黑树(Red-Black tree)算法的实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的Comparator进行排序

TreeMap();			//使用键的自然顺序构造一个新的、空的树映射
TreeMap(Comparator<? super K> comparator);		//构造一个新的、空的树映射,该映射根据给定比较器进行排序

此实现为containsKey()、get()、put()和remove()操作提供受保证的log(n)时间开销。

哈希映射 HashMap

该类是基于哈希表的Map接口的实现。此实现提供所有可选的映射操作,并允许使用null值和null键(除了非同步和允许使用null之外,HashMap类与Hashtable大致相同)。此类不保证映射的顺序,特别是它不保证该顺序恒久不变

HashMap的实例有两个参数影响其性能:初始容量和加载因子。容量是哈希表中的桶的数量,初始容量只是哈希表在创建时的容量。加载因子是哈希表在其容量自动添加之前可以达到多满的一种尺度。

链表哈希映射 LinkedHashMap

该类是Map接口的哈希表和链表列表实现的,具有可预知的迭代顺序。此实现与HashMap的不同之处在于后者维护着一个运行于所有条目的双重链接列表,此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)

  • initialCapacity:初始容量

  • loadFactor:加载因子

  • accessOrder:排序模式,如果对于访问顺序,则为 true;如果对于插入顺序,则为false

    此实现不是同步的,如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用Collections.synchronizedMap()方法来包装该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问:

    Map m = Collections.synchronizedMap(...);
    

弱哈希映射 WeakHashMap

该类是以弱键实现的基于哈希表的Map。在 WeakHashMap中,当某个键不再正常使用时,将自动移除其条目。

null 值和null键都被支持。

WeakHashMap 类的行为部分取决于垃圾回收器的动作,所以,几个常见的Map常量不支持此类。因为垃圾回收器在任何时候都可能丢弃键,WeakHashMap就像是一个被悄悄移除条目的未知线程。即使对WeakHashMap实例进行同步,并且没有调用任何赋值方法,在一段时间后size()方法也可能返回较小的值,对于isEmpty()方法,返回false,然后返回true,对于给定的键,containsKey()方法返回true然后返回false,而get()方法返回一个值,但接着返回null,对于以前出现在映射中的键,put方法返回 null,而remove()返回false,对于键Set值Collection和条目Set进行的检查,生成的元素越来越少。

WeakHashMap 中的每个键对象间接地存储为一个弱引用的指引对象。因此,不管是在映射内还是在映射之外,只有在垃圾回收器消除某个键的弱引用之后,该键才会自动移除

哈希表 Hashtable

此类实现一个哈希表,该哈希表将键映射到相应的值。任何非null对象都可以用做键或值。为了成功地在哈希表中储存和获取对象,用做键的对象必须实现hashCode方法和equals方法

Hashtable的实例有两个参数影响其性能:初始容量和加载因子。容量是哈希表中桶的数量,初始容量就是哈希表创建时的容量。

初始容量主要控制时间消耗与执行rehash操作所需要的时间损耗之间的平衡。如果初始容量大于Hashtable 所包含的最大条目数除以加载因子,则永远不会发生rehash 操作。但是,将初始容量设置太高可能会浪费空间

Hashtable 同步的

属性 Properties

Properties类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。一个属性列表可包含另一个属性列表作为它的"默认值";如果未能在原有的属性列表中搜索到属性键,则搜索第二个属性列表

Properties 继承于Hashtable

Properties 主要提供了对属性的读取和写入功能

此类是线程安全的:多个线程可以共享单个Properties 对象而无需进行外部同步

对比与选择

  • List:一系列数据的列表,允许重复
  • Set:一系列数据的集合,不允许重复
  • Map:一系列映射列表——键值对

对比

  • 如果涉及到堆栈、队列等操作,应该考虑用List中的Stack、Queue
  • 对于需要快速插入、删除元素,应该使用LinkedList
  • 如果需要快速随机访问元素,应该使用ArrayList
  • 如果程序在单线程环境中,或者访问仅仅在一个线程中进行,应该考虑非同步的类,其效率较高
  • 如果多个线程可能同时操作一个类,应该使用同步的类