11、集合


在任意时刻和任意位置创建任意数量的对象。
数组:具有固定的大小尺寸。
集合类:基本的类型有 List 、 Set 、 Queue 和 Map。这些类型也被称作容器类(container classes)。
Set 对于每个值都只保存一个对象。
Map 是一个关联数组,允许将某些对象与其他对象关联起来。
Java集合类都可以自动地调整自己的大小。

1、类型安全的集合

通过使用泛型,就可以在编译期防止将错误类型的对象放置到集合中。
类型推断(type inference)

2、基本概念

Java集合类库采用“持有对象”(holding objects)的思想,并将其分为两个不同的概念,表示为类库的基本接口:

  • 集合(Collection) :一个独立元素的序列,这些元素都服从一条或多条规则。List 必须以插入的顺序保存元素, Set 不能包含重复元素, Queue 按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
  • 映射(Map) : 一组成对的“键值对”对象,允许使用键来查找值。 ArrayList 使用数字来查找对象,因此在某种意义上讲,它是将数字和对象关联在一起。 map 允许我们使用一个对象来查找另一个对象,它也被称作关联数组(associative array),因为它将对象和其它对象关联在一起;或者称作字典(dictionary),因为可以使用一个键对象来查找值对象,就像在字典中使用单词查找定义一样。 Map 是强大的编程工具。

Collection 接口概括了序列的概念——一种存放一组对象的方式。

3、添加组元素

Arrays.asList() 方法接受一个数组或是逗号分隔的元素列表(使用可变参数),并将其转换为 List 对象。 Collections.addAll() 方法接受一个 Collection 对象,以及一个数组或是一个逗号分隔的列表,将其中元素添加到 Collection 中。

4、集合的打印

Collection 类型在每个槽中只能保存一个元素。此类集合包括: List ,它以特定的顺序保存一组元素; Set ,其中元素不允许重复; Queue ,只能在集合一端插入对象,并从另一端移除对象。

Map 在每个槽中存放了两个元素,即键和与之关联的值。键和值保存在 HashMap 中的顺序不是插入顺序,因为 HashMap 实现使用了非常快速的算法来控制顺序。 TreeMap 通过比较结果的升序来保存键, LinkedHashMap 在保持 HashMap 查找速度的同时按键的插入顺序保存键。

5、List

  • 基本的 ArrayList ,擅长随机访问元素,但在 List 中间插入和删除元素时速度较慢.
  • LinkedList 对于随机访问来说相对较慢,通过代价较低的在 List 中间进行的插入和删除操作,提供了优化的顺序访问。

5.1 ArrayList

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

size、isEmpty、get、set、iterator和listIterator操作在常数时间内运行。
add操作在平摊常数时间(amortized constant time)内运行,也就是说,添加n个元素需要O(n)个时间。

每个ArrayList实例都有一个容量。容量是用于存储列表中的元素的数组的大小。它总是至少与列表大小一样大。当元素被添加到ArrayList中时,它的容量会自动增长。 The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost.

在添加大量元素之前,使用ensureCapacity操作,应用程序可以增加ArrayList实例的容量。这可以减少增加的从新分配的数量。

注意,这个实现不是同步的。
如果多个线程同时访问一个ArrayList实例,并且至少有一个线程从结构上修改了这个列表,那么它必须在外部进行同步。(结构修改是添加或删除一个或多个元素,或显式调整背景数组大小的任何操作;仅仅设置元素的值不是结构修改。)这通常是通过对一些自然封装列表的对象进行同步来实现的。如果不存在这样的对象,则应该使用 Collections.synchronizedList方法对列表进行“包装”。

List list = Collections.synchronizedList(new ArrayList(...));

这个类的iterator和listIterator方法返回的迭代器是快速失效的:如果在创建迭代器之后的任何时候,以任何方式(除了通过迭代器自己的删除或添加方法)对列表进行结构修改,迭代器将抛出ConcurrentModificationException异常。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在将来某个不确定的时间冒任意的、不确定的行为的风险。

注意,不能保证迭代器的快速故障行为,因为通常来说,在存在非同步并发修改的情况下,不可能做出任何严格的保证。因此,编写一个依赖于这个异常的正确性的程序是错误的:迭代器的快速故障行为应该只用于检测bug。

5.2 、LinkedList

双链表实现了列表和Deque接口。实现所有可选的列表操作,并允许所有元素(包括null)。
所有操作的执行都符合双链表的预期。索引到列表中的操作将从头或尾遍历列表,以更接近指定索引的操作为准。
LinkedList 还添加了一些方法,使其可以被用作栈、队列或双端队列(deque)

注意,这个实现不是同步的。
如果多个线程同时访问一个linked list实例,并且至少有一个线程从结构上修改了这个列表,那么它必须在外部进行同步。(结构修改是添加或删除一个或多个元素,或显式调整背景数组大小的任何操作;仅仅设置元素的值不是结构修改。)这通常是通过对一些自然封装列表的对象进行同步来实现的。如果不存在这样的对象,则应该使用 Collections.synchronizedList方法对列表进行“包装”。

List list = Collections.synchronizedList(new ArrayList(...));

这个类的iterator和listIterator方法返回的迭代器是快速失效的:如果在创建迭代器之后的任何时候,以任何方式(除了通过迭代器自己的删除或添加方法)对列表进行结构修改,迭代器将抛出ConcurrentModificationException异常。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在将来某个不确定的时间冒任意的、不确定的行为的风险。

注意,不能保证迭代器的快速故障行为,因为通常来说,在存在非同步并发修改的情况下,不可能做出任何严格的保证。因此,编写一个依赖于这个异常的正确性的程序是错误的:迭代器的快速故障行为应该只用于检测bug。

  • getFirst()element() 是相同的,它们都返回列表的头部(第一个元素)而并不删除它,如果 List 为空,则抛出 NoSuchElementException 异常。 peek() 方法与这两个方法只是稍有差异,它在列表为空时返回 null 。
  • removeFirst()remove() 也是相同的,它们删除并返回列表的头部元素,并在列表为空时抛出 NoSuchElementException 异常。 poll() 稍有差异,它在列表为空时返回 null 。
  • addFirst() 在列表的开头插入一个元素。
  • offer()add()addLast() 相同。 它们都在列表的尾部(末尾)添加一个元素。
  • removeLast() 删除并返回列表的最后一个元素

6、迭代器

迭代器是一个对象,它在一个序列中移动并选择该序列中的每个对象,而客户端程序员不知道或不关心该序列的底层结构。
Java 的 Iterator 只能单向移动。这个 Iterator 只能用来:

  • 使用 iterator() 方法要求集合返回一个 Iterator。 Iterator 将准备好返回序列中的第一个元素。
  • 使用 next() 方法获得序列中的下一个元素。
  • 使用 hasNext() 方法检查序列中是否还有元素。
  • 使用 remove() 方法将迭代器最近返回的那个元素删除。

能够将遍历序列的操作与该序列的底层结构分离。

6.1、ListIterator

只能由各种 List 类生成,ListIterator 可以

  • 双向移动

  • 生成相对于迭代器在列表中指向的当前位置的后一个和前一个元素的索引

  • 使用 set() 方法替换它访问过的最近一个元素

  • 通过调用 listIterator() 方法来生成指向 List 开头处的 ListIterator

  • 通过调用 listIterator(n) 创建一个一开始就指向列表索引号为 n 的元素处的 ListIterator

8、Stack

堆栈是“后进先出”(LIFO)集合,ArrayDeque ,其中包含直接实现堆栈功能的方法。是Deque接口的可调整大小的数组实现。

Array deques没有容量限制;它们根据需要增长以支持使用。它们不是线程安全的;在缺乏外部同步的情况下,它们不支持多线程的并发访问。禁止空元素。这个类用作堆栈时可能比Stack快,用作队列时可能比LinkedList快。

大部分的矩阵运算都是在平摊常数时间内进行的。除了remove、removeFirstOccurrence、removeLastOccurrence、contains、iterator.remove()和bulk操作,这些操作都在线性时间内运行。

这个类的迭代器方法返回的迭代器是快速失效的:如果deque在迭代器创建后的任何时候被修改,除了通过迭代器自己的remove方法之外的任何方式,迭代器通常会抛出ConcurrentModificationException。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在将来某个不确定的时间冒任意的、不确定的行为的风险。

注意,不能保证迭代器的快速故障行为,因为通常来说,在存在非同步并发修改的情况下,不可能做出任何严格的保证。故障快速迭代器在最大努力的基础上抛出ConcurrentModificationException。因此,编写一个依赖于这个异常的正确性的程序是错误的:迭代器的快速故障行为应该只用于检测bug。

该类及其迭代器实现集合和迭代器接口的所有可选方法。
该类是Java集合框架的成员。

9、Set

Set 最常见的用途是测试归属性,可以很轻松地询问某个对象是否在一个 Set 中。
Set 就是一个 Collection ,只是行为不同。

  • TreeSet 将元素存储在红-黑树数据结构中。

  • HashSet 使用散列函数。

  • LinkedHashSet 因为查询速度的原因也使用了散列,但是看起来使用了链表来维护元素的插入顺序。

最常见的操作之一是使用 contains() 测试成员归属性。

10、Map

将对象映射到其他对象的能力是解决编程问题的有效方法。
Map 与数组和其他的 Collection 一样,可以轻松地扩展到多个维度,只需要创建一个值为 Map 的 Map(这些 Map 的值可以是其他集合,甚至是其他 Map)。因此,能够很容易地将集合组合起来以快速生成强大的数据结构。

11、Queue

队列是一个典型的“先进先出”(FIFO)集合。队列在并发编程中尤为重要,因为它们可以安全地将对象从一个任务传输到另一个任务。

LinkedList 实现了 Queue 接口,并且提供了一些方法以支持队列行为,因此 LinkedList 可以用作 Queue 的一种实现。

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

11.1、PriorityQueue

优先级队列声明下一个弹出的元素是最需要的元素(具有最高的优先级)。
当在 PriorityQueue 上调用 offer() 方法来插入一个对象时,该对象会在队列中被排序。默认的排序使用队列中对象的自然顺序(natural order),但是可以通过提供自己的 Comparator 来修改这个顺序。 PriorityQueue 确保在调用 peek() , poll() 或 remove() 方法时,获得的元素将是队列中优先级最高的元素。
优先级队列算法通常会按插入顺序排序(维护一个堆),但它们也可以在删除时选择最重要的元素。 如果对象的优先级在它在队列中等待时可以修改,那么算法的选择就显得很重要了。

Collection 是所有序列集合共有的根接口。使用接口描述的一个理由是它可以使我们创建更通用的代码。
实现 Collection 就意味着需要提供 iterator() 方法。

12、集合与迭代

Collection 是所有序列集合共有的根接口。

当需要实现一个不是 Collection 的外部类时,由于让它去实现 Collection 接口可能非常困难或麻烦,因此使用 Iterator 就会变得非常吸引人。如果实现了 Collection ,就必须实现 iterator()。这时,继承并提供创建迭代器更好。

13、for-in和迭代器

for-in 语句适用于数组或其它任何 Iterable。Java 5 引入了一个名为 Iterable 的接口,该接口包含一个能够生成 Iterator 的 iterator() 方法。for-in 使用此 Iterable 接口来遍历序列。

尝试将数组作为一个 Iterable 参数传递会导致失败。这说明不存在任何从数组到 Iterable 的自动转换; 必须手工执行这种转换。

小结

Java 提供了许多保存对象的方法:

  • 数组将数字索引与对象相关联。它保存类型明确的对象,因此在查找对象时不必对结果做类型转换。它可以是多维的,可以保存基本类型的数据。虽然可以在运行时创建数组,但是一旦创建数组,就无法更改数组的大小。

  • Collection 保存单一的元素,而 Map 包含相关联的键值对。使用 Java
    泛型,可以指定集合中保存的对象的类型,因此不能将错误类型的对象放入集合中,并且在从集合中获取元素时,不必进行类型转换。各种
    Collection 和各种 Map
    都可以在你向其中添加更多的元素时,自动调整其尺寸大小。集合不能保存基本类型,但自动装箱机制会负责执行基本类型和集合中保存的包装类型之间的双向转换。

  • 像数组一样, List 也将数字索引与对象相关联,因此,数组和 List 都是有序集合。

  • 如果要执行大量的随机访问,则使用 ArrayList ,如果要经常从表中间插入或删除元素,则应该使用 LinkedList 。

  • 队列和堆栈的行为是通过 LinkedList 提供的。

  • Map 是一种将对象(而非数字)与对象相关联的设计。 HashMap 专为快速访问而设计,而 TreeMap
    保持键始终处于排序状态,所以没有 HashMap 快。 LinkedHashMap 按插入顺序保存其元素,但使用散列提供快速访问的能力。

  • Set 不接受重复元素。 HashSet 提供最快的查询速度,而 TreeSet 保持元素处于排序状态。 LinkedHashSet
    按插入顺序保存其元素,但使用散列提供快速访问的能力。

  • 不要在新代码中使用遗留类 Vector ,Hashtable 和 Stack 。
    在这里插入图片描述在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章