Java Collections框架简介

An Introduction to the Java Collections Framework

本文是Marcus Biel的免费Java 8课程的一部分,该课程侧重于干净的代码原理。 在本文中,您将对Java Collections Framework(JCF)进行高级介绍。 不幸的是,集合是一个带有多个重载含义的词。 为了使事情更清晰,我们将首先预先讨论该词的含义。

此处也提供该文章的PDF。

<iframex height = 400 src="https://www.youtube.com/embed/4wbFRItSoYE" frameBorder = 0 width = 600 allowfullscreen ="> </iframex>

集合可以参考:

  • 它的日常含义是"一组或一组事物"

    它的日常含义是"一组或一组事物"

    组成Java Collections Framework的接口和类的集合,

    数据结构,例如盒子或容器,可以容纳一组对象,例如数组,

    util.Collection 接口,这是两个主要JCF接口之一,或者

    util.Collections ,一个实用程序类,可以帮助修改或操作Java集合。

    本文基于《 OCA / OCP学习指南》的第11章,该书包含有关Java编程的知识。 作为作者Kathy Sierra和Bert Bates的忠实拥护者,即使您不打算成为一名合格的Java程序员,我也建议您阅读本书。

    那么,从高级的角度来看,什么是Java Collections Framework? 首先,它实际上是通用接口和类的库或工具箱。 该工具箱包含各种集合接口和类,它们是数组的更强大的,面向对象的替代品。 与集合相关的实用程序接口和类也使更好的易用性。

    总览

    在本节中,我们将深入研究集合的接口和类层次结构。 与数组不同,所有集合的大小都可以动态增长或缩小。 正如我之前所说,集合包含对象组。 A Map可以将高度相关的对象对存储在一起,每对对象由a key和a value组成。 值在地图中没有特定位置,但可以使用与其配对的键进行检索。 不用担心现在太多了,我们稍后将详细介绍。

    class-and-interface-hierarchy

    图1显示了扩展或实现Collection接口的类和接口的层次结构-至少熟悉那里列出的名称将很有用。 Collection接口位于许多子接口和实现类的顶部。集合可以用提供SetListQueue的不同方式保存一组对象。集合定义为一组唯一的对象。唯一的定义是由其持有的对象类型的equals方法定义的。换句话说,集合不能容纳两个相等的对象。 A List被定义为对象序列。与集合相反,列表可以包含重复的条目。它还按插入的顺序保留其元素。队列有两个方面。将条目添加到其尾端,同时从顶部或头部删除条目。通常将其描述为"先进先出"(FIFO),这通常与现实生活中的排队很像,即排队的第一个人是第一个离开队列的人。

    设置界面

    HashSet,LinkedHasSet和TreeSet

    HashSetLinkedHashSetTreeSetSet的实现,位于图1中Collection接口层次结构的左端附近。HashSet是大多数情况下使用的默认实现。LinkedHashSet就像 HashSetList的组合,因为它不允许像Set那样重复条目,但是像List那样按插入的顺序遍历其元素。TreeSet将不断保持 其所有元素都按某种排序的顺序排列。 但是请记住,没有免费的午餐之类的东西,并且每个增加的功能都需要一定的费用。

    sortedSet和Navigable Set

    看完实现Set的三个类之后,让我们看一下我们还没有讨论过的两个子接口。 顾名思义,sortedSet是一个Set,具有始终被排序的属性。 Java 6中添加的NavigableSet接口使我们能够浏览排序列表,提供检索大于或小于给定元素Set的下一个元素的方法。

    列表界面

    ArrayList和LinkedList

    ArrayListList的默认实现,位于图1的集合层次结构的中间。与any List实现一样,它确实允许重复元素和按插入顺序进行迭代。 由于它是基于数组的,因此迭代和读取的速度非常快,但是在随机位置添加或删除元素的速度非常慢,因为它必须重建底层的数组结构。 相反,LinkedList使得在列表中的任何位置添加或删除元素变得容易,而在随机位置读取则较慢。

    在引导程序上

    附带说明一下,我们不久将考虑Vector,它是自JDK 1起就存在的类,甚至早于Java 2添加的Collections Framework之前。总之,它的性能不是最佳的,因此没有新的代码。 应该曾经使用过。 ArrayListLinkedList仅做得更好。

    队列接口

    最后,我们看一下实现Queue的类。 关于LinkedList还要提到的另一件事是,尽管它实现了List,但实际上它也实现了Queue。 这样做是基于以下事实:它的实际实现是一个双向链接列表,因此也很容易实现Queue接口。


    PriorityQueue
    <铅>

    除了LinkedList,另一个常见的Queue实现是PriorityQueue。 它是一种自动保持其元素顺序的实现。 它具有与TreeSet类似的功能,不同之处在于它允许重复的条目。

    地图界面

    现在我们来看一下Map接口,奇怪的是它与Collection接口没有关系。 A Collection在一个实体上操作,而a Map在两个实体上操作:唯一密钥,例如 车辆识别号和与钥匙有关的物体,例如 一辆车。 要从a Map中检索对象,通常会使用其键。Map是许多接口和类的根,如图2所示。

    hashtable,HashMap和LinkedHashMap

    hashtable类是Java 1中第一个基于哈希表数据结构的Collection。 不幸的是,像Vector一样,该类由于性能欠佳而被弃用。 我们可以忘记它,而使用其他Map实现。HashMap是大多数情况下会使用的默认实现。

    通常,A Map对其内部存储元素的方式不做任何保证。 但是,此规则的一个例外是LinkedHashMap,它允许我们按插入顺序迭代映射。

    Map-interface-1

    sortedMap

    让我们看一下扩展Map的接口。 顾名思义,sortedMap扩展了Map并定义了一个连续排序的地图的协定。NavigableMap进一步扩展了它,增加了导航已排序地图的方法。 例如,这使我们可以使所有条目小于或大于某个条目。 实际上,MapSet层次结构之间有许多相似之处。 原因是Set实现实际上在内部由Map实现支持。

    地图界面

    现在,让我们看一下地图界面。此接口与Collection接口无关。 A Collection在一个实体上运行,而地图在两个实体上运行-唯一键(例如,车辆识别号)和与该键相关的对象(例如,汽车对象)。借助键,您可以检索与之相关的对象。接口映射是许多接口和类的基础,我们现在将对其进行研究。类hashtable是Java JDK1中基于数据结构哈希表的第一个集合,因此Java创建者将其称为hashtable。不幸的是,这使得很难区分两者。像Vector一样,该类由于性能欠佳而被弃用,因此让我们删除它,然后将其遗忘。相反,请使用其他实现映射接口的类之一。HashMap是大多数情况下应使用的默认实现。地图通常不保证其内部存储元素的方式。该规则的一个例外是LinkedHashMap,它允许它按插入顺序迭代映射。最后但并非最不重要的是,TreeMap是一个不断排序的映射。

    Image title

    sortedMap

    现在,让我们看一下扩展Map接口的接口。顾名思义,interface sortedMap扩展了Map接口并定义了一个连续排序的Map的协定。NavigableMap再次扩展了sortedMap interface并添加了在地图中导航的方法。例如,这允许您检索小于或大于给定条目的所有条目。实际上,MapSet层次结构之间有许多相似之处。原因是Set实现实际上在内部由Map实现支持。最后但并非最不重要的一点是,您可能已经注意到Java Collection类通常包含其名称所基于的数据结构。要针对给定情况选择最佳集合,您必须先比较数据结构(如array,LinkedList,hashtableTree)的特定特征。简而言之,没有一个最佳选择,每个选择都有其自身的优缺点。我保证在以后的一集中谈论这个非常令人兴奋的话题。敬请关注。 CollectionMap类的概述仅向您展示了整个故事的一部分。在以后的文章中,我将向您介绍Java Collections Framework的并发容器。

    sortedMap

    大图

    您可能已经注意到Java的Collection类通常包含基于其名称的数据结构。 要针对给定情况选择最佳集合,您必须比较和匹配数据结构(如LinkedListhashtableTreeSet)的属性与当前问题。 简而言之,没有一个最佳选择,因为每个选择都有其自身的优缺点。 实际上,这方面还有很多基础,因为此概述仅显示了Collection和Map类的巨大范围中的一小部分。 实际上,Java Collections Framework中甚至还有并发容器,这些容器用于并发编程。

    泛型

    泛型的主题至少与Java Collections Framework一样广泛。 在本文的上下文中,我们将仅讨论了解集合框架所需的最低要求。 在简要概述之后,有很多未解决的问题都是可以的。 一切都会一一解释。

    1
    List<String> myList = new ArrayList<String>(100);

    注意尖括号的用法。 在左侧,我们用尖括号中的String参数定义了一个List变量myList。 我们告诉编译器myList变量只能引用某些包含字符串的列表。 然后,我们创建一个类型为ArrayList的对象,并再次告诉编译器该列表应仅包含字符串。 换句话说,这就是使容器具有类型安全性的原因。 另请注意,变量使用List类型而不是ArrayList。 这使我们的代码更加灵活。 您将只创建一次对象,但是您最终常常会在许多地方使用它。 话虽这么说,当您声明一个List而不是一个ArrayList时,您将后来用一个LinkedList替换了ArrayList,而您所要做的就是更改一行代码。

    1
    Collection<String> myList = new ArrayList<String>(100);

    如果您确实不需要特定于List的方法,则也可以只使用Collection。最好始终使用最不具体,最小的接口作为变量。注意使用100作为ArrayList构造函数参数。在这种情况下,这是性能优化。由于ArrayList和所有基于hashtable的集合在内部在Arrays上进行操作,因此当此类集合的大小增大时,它将即时创建更大的数组,并将所有内容从旧数组传输到新数组。尽管这需要花费一些额外的时间,但现代硬件是如此之快,以至于通常这不是问题。另一方面,知道集合的确切大小或近似大小总比确定默认集合大小好。了解Java集合所基于的数据结构有助于更好地理解这种情况下的性能。注意如此小的细节通常是常规开发人员和软件工匠之间的区别。

    1
    Map<VIN, Car> myMap = new HashMap<>(100);

    查看上面如何声明Map以及如何构造HashMap。映射是一个标识关键元素与一个值元素之间的关系,两者都可以是不同的类型。在上面的示例中,VIN(车辆识别号)用作键,而Car对象是值。类型参数以逗号分隔列表的形式添加在尖括号中。从Java 7开始,如果声明变量并在同一行中全部创建对象,则可以将第二对尖括号留空,因为编译器会从参考变量的泛型类型推断出对象的类型。空尖括号被称为菱形运算符,这是由于空尖括号如何形成菱形而得名。到目前为止,仅讨论了泛型类的用法,但是,我们锁定了要在实例化中使用的具体类型参数。仅当某些方法界面被定义为预先以通用方式使用。

    编写通用代码

    清单1显示了一个通用定义的接口。 在第一行中,接口被定义为对两种泛型类型进行操作的接口,稍后必须指定这些泛型。 锁定这些类型后,将自动指定接口方法使用的类型。 如果在代码中看到一个字母的类型,则可能意味着可以通用的方式使用它。

    1
    2
    3
    4
    public interface MyInterface<E, T> {
    E read();
    void process(T o1, T o2);
    }

    清单1

    其他实用程序接口

  • util.Iterator

    util.Iterator

    lang.Iterable

    lang.Comparable

    lang.Comparator

    上面列出了Java Collections Framework的一些其他实用程序接口。 它们通常由框架或JDK的类实现。 此外,它们也可以由您自己的类通过利用Collections Framework的功能和与之互操作性来实现。 严格来说, java.lang.Iterable 不是框架的一部分,而是更精确地位于框架之上。 它是 java.util.Collection 的超级接口,这意味着每个实现Collection的类也都实现了 java.lang.Iterable

    java.util.Iterator

  • boolean hasNext();

  • E next();

  • void remove();

  • An Iterator是一个对象,它像遥控器一样,经常迭代对象。hasNext()如果集合中包含更多元素,则返回true,next() 返回迭代中的下一个元素,而remove()删除对象 Iterator从其基础集合返回的最后一个元素。

    java.lang.Iterable

  • Iterator Iterator()

    Iterator Iterator()

    Iterable仅提供一种返回Iterator的方法。 实现此接口的每个Collection都可以在 for-each循环中使用,大大简化了自制集合的使用。 要使自己的集合使用for-each循环,您只需执行两个简单的步骤:首先,为集合编写一个Iterator并实现其所有方法hasNext next和remove。 其次,通过添加迭代器方法来实现Iterable接口,该方法返回您在第一步中编写的迭代器实现的实例。

    java.lang.Comparable

  • int compareTo(T o)

    int compareTo(T o)

    实现Comparable为您的实体定义自然排序顺序。 该接口仅包含您需要实现的一种方法,即compareTo,该方法将您的Comparable TO (代表另一个相同类型的实体)进行比较。 如果对象小于给定参数 o ,则返回负整数;如果对象等于 o ,则返回0;如果对象大于 o

    一件事小于或大于另一件事意味着要定义。 对于数字,很容易得出1小于5的结果。但是颜色呢? 这完全取决于您认为实体的自然排序。 例如,将Comparable对象放入TreeSetTreeMap时,它将使用定制的compareTo方法自动对集合中的所有元素进行排序。 如您所见,Java Collections Framework在设计时考虑到扩展性,为您提供了很多插入自己的类的可能性。

    java.lang.Comparator

  • int compare(T o1, T o2)

    int compare(T o1, T o2)

    此接口与Comparable非常相似。 它允许您定义其他排序顺序,例如 相反的顺序。 排序逻辑未在您的实体类中直接实现。 相反,它是在外部排序策略类中定义的,可以选择将其附加到Collection或排序方法以为实体集合定义其他排序顺序。 对Comparable的接口协定应用相同的规则:如果第一个参数o1小于第二个参数o2,则返回负整数;如果两个参数相等,则返回0;如果o1大于o2,则返回正整数。

    集合和数组

    最后但并非最不重要的一点,我们看一下两个实用程序类 java.util.Collections java.util.Arrays 。 就像瑞士军刀一样,两者都提供了静态辅助方法,大大提高了Collection类的通用性。 集合提供诸如sortshufflereversesearchminmax的方法。Arrays实际上与Collections非常相似,不同之处在于它们在原始数组上运行,即 例如,允许我们对数组进行排序或搜索。

    如果您喜欢本文,并且想了解有关Java集合的更多信息,请查看有关Java集合所有内容的教程和文章的集合。