Java核心技术卷I:基础知识(原书第8版):13.1 集合接口

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

铁文整理

13.1 集合接口

    Java最初版本只为最常用的数据结构提供了很少的一组类:VectorStackHashtableBitSetEnumeration接口,其中的Enumeration接口提供了一种用于访问任意容器中各个元素的抽象机制。这是一种很明智的选择,但要想建立一个全面的集合类库还需要大量的时间和高超的技能。

    随着Java SE 1.2的问世,设计人员感到是推出—组功能完善的数据结构的时机了。面对一大堆相互矛盾的设计策略,他们希望让类库规模小且易于学习而不希望像C++的标准模版库那样复杂,但却又希望能够得到STL率先推出的“泛型算法”所具有的优点。他们希望将传统的类融入新的框架中。与所有的集合类库设计者一样,他们必须做出一些艰难的选择。于是,在整个设计过程中,他们做出了一些独具特色的设计决定。本节将介绍Java集合框架的基本设计,展示使用它们的方法,并解释一些颇具争议的特性背后的考虑。

13.1.1 将集合的接口与实现分离

    与现代的数据结构类库的常见情况一样,Java集合类库也将接口与实现分离。首先,看一下人们熟悉的数据结构——队列(queue)是如何分离的。

    队列接口指出可以在队列的尾部添加元素,在队列的头部删除元素,并且可以査找队列中元素的个数。当需要收集对象,并按照“先进先出”的规则检索对象时就应该使用队列(见图13-1)。

    一个队列接口的最小形式可能类似下面这样:

interface Queue<E> // a simplified form of the interface in the standard library

{

    void add(E dement);

    E remove();

    int size();

}

    这个接口并没有说明队列是如何实现的。队列通常有两种实现方式:一种是使用循环数组;另一种是使用链表(见图13-2)。

    注释:从Java SE 5.0开始,集合类是带有类型参数的泛型类。有关泛型类的更多信息请参看第12章。

    每一个实现都可以通过一个实现了Queue接口的类表示。

class CircularArrayQueue<E> implements Queue<E> // not an actual library class

{

    CircularArrayQueue(int capacity) { }

    public void add(E element) { }

    public E remove() { }

    public int size() { }

 

    private E[] elements;

    private int head;

    private int tail;

}

 

class LinkedListQueue<E> implements Queue<E> // not an actual library class

{

    LinkedListQueue() { }

    public void add(E element) { }

    public E remove() { }

    public int size() { }

 

    private Link head;

    private Link tail;

}

    注释:实际上,Java类库没有名为CircularArrayQueue和LinkedListQueue的类。这里,只是以这些类作为示例,解释—下集合接口与实现在概念上的不同。如果需要一个循环数组队列,就可以使用Java SE 6中引入的ArrayDeque类。如果需要一个链表队列,就直接使用LinkedList类,这个类实现了Queue接口。

    当在程序中使用队列时,一旦构建了集合就不需要知道究竟使用了哪种实现。因此,只有在构建集合对象时,使用具体的类才有意义。可以使用接口类型存放集合的引用。

        Queue<Customer> expressLane = new CircularArrayQueue<Customer>(100);

        expressLane.add(new Customer("Harry"));

    利用这种方式,一旦改变才想法,可以轻松地使用另外—种不同的实现。只需要对程序的一个地方做出修改,即调用构造器的地方。如果最终觉得LinkedListQueue是个更好的选择,就将代码修改为:

        Queue<Customer> expressLane = new LinkedListQueue<Customer>();

        expressLane.add(new Customer("Harry"));

    为什么选择这种实现,而不选择那种实现呢?接口本身并不能说明哪种实现的效率究竟如何。循环数组要比链表更高效,因此多数人优先选择循环数组。然而,通常这样做也需要付出一定的代价。

    循环数组是一个有界集合,即容量有限。如果程序中要收集的对象数量没有上限,就最好使用链表来实现。

    在研究API文档时,会发现另外一组名字以Abstract开头的类,例如,AbstractQueue。这些类是为类库实现者而设计的。如果想要实现自己的队列类(也许不太可能),会发现扩展AbstractQueue类要比实现Queue接口中的所有方法轻松得多。

13.1.2 Java类库中的集合接口和迭代器接口

    Java中,集合类的基本接口是Collection接口。

    这个接口有两个基本方法:

public interface Collection<E> {

    boolean add(E element);

    Iterator<E> iterator();

}

    除了这两个方法之外,还有几个方法,将在稍后介绍。

    add方法用于向集合中添加元素。如果添加元素确实改变了集合就返回true,如果集合没有发生变化就返回false。例如,如果试图向集中添加一个对象,而这个对象在集合中已经存在,这个添加请求就没有实效,因为集中不允许有重复的对象。

    iterator方法用于返回一个实现了Iterator接口的对象。可以使用这个迭代器对象依次访问集合中的元素。

1. 迭代器

    Iterator接口包含3个方法:

public interface Iterator<E> {

    E next();

    boolean hasNext();

    void remove();

}

    通过反复调用next方法,可以逐个访问集合中的每个元素。但是,如果到达了集合的末尾,next方法将抛出一个NoSuchElementException。因此,需要在调用next之前调用hasNext方法。如果迭代器对象还有多个供访问的元素,这个方法就返回true。如果想要査看集合中的所有元素,就请求一个迭代器,并在hasNext返回true时反复地调用next方法。例如:

        Collection<String> c = ...;

        Iterator<String> iter = c.iterator();

        while (iter.hasNext()) {

            String element = iter.next();

            doSomethingWith(element);

        }

    Java SE 5.0起,这个循环可以采用一种更优雅的缩写方式。用“for each”循环可以更加简练地表示同样的循环操作:

        for (String element : c) {

            doSomethingWith(element);

        }

    编译器简单地将“for each”循环翻译为带有迭代器的循环。

    for each”循环可以与任何实现了Iterable接口的对象一起工作,这个接口只包含一个方法:

public interface Iterable<E> {

    Iterator<E> iterator();

}

    Collection接口扩展了Iterable接口。因此,对于标准类库中的任何集合都可以使用“for each”循环。

    元素被访问的顺序取决于集合类型。如果对ArrayList进行迭代,迭代器将从索引0开始,每迭代一次,索引值加1。然而,如果访问HashSet中的元素,每个元素将会按照某种随机的次序出现。虽然可以确定在迭代过程中能够遍历到集合中的所有元素,但却无法预知元素被访问的次序。这对于计算总和或统计符合某个条件的元素个数这类与顺序无关的操作来说.并不是什么问题。

    注释:编程老手会注意到:Iterator接口的nexthasNext方法与Enumeration接口的nextElementshasMoreElements方法的作用一样。Java集合类库的设计者可以选择使用Enumeration接口。但是,他们不喜欢这个接口累赘的方法名,于是引入了具有较短方法名的新接口。

    Java集合类库中的迭代器与其他类库中的迭代器在概念上有着重要的区别。在传统的集合类库中,例如,C++的标准模版库,迭代器是根据数组索引建模的。如果给定这样一个迭代器,就可以査看指定位置上的元素,就像知道数组索引就可以査看数组元素a[i]一样。不需要查找元素,就可以将迭代器向前移动一个位置。这与不需要执行査找操作就可以通过i++将数组索引向前移动一样。但是,Java迭代器并不是这样操作的。查找操作与位置变更是紧密相连的。査找一个元素的唯一方法是调用next,而在执行査找操作的同时,迭代器的位置随之向前移动。

    因此,应该将Java迭代器认为是位于两个元素之间。当调用next时,迭代器就越过下一个元素,并返回刚刚越过的那个元素的引用(见图13-3)。

    注释:这里还有一个有用的类推。可以将Iterator.nextInputStream.read看作为等效的。从数据流中读取一个字节,就会自动地“消耗掉”这个字节。下一次调用read将会消耗并返回输入的下一个字节。用同样的方式,反复地调用next就可以读取集合中所有元素。

2. 删除元素

    Iterator接口的remove方法将会删除上次调用此以方法时返回的元素,在大多数情况下,在决定刪除某个元素之前应该先看一下这个元素是很具有实际意义的。然而,如果想要删除指定位置上的元素,仍然需要越过这个元素。下面是如何删除字符串集合中第一个元素的方法:

        Iterator<String> it = c.iterator();

        it.next(); // skip over the first element

        it.remove(); // now remove it

    更重要的是,对next方法和remove方法的调用具有互相依赖性,如果调用remove之前没有调用next将是不合法的。如果这样做,将会抛出一个IllegalStateException异常。

    如果想删除两个相邻的元素,不能直接地这样调用:

        it.remove();

        it.remove(); // ERROR

    相反地,必须先调用next越过将要刪除的元素。

        it.remove();

        it.next();

        it.remove(); // OK

3. 泛型实用方法

    由于Collection都是泛型接口,可以编写操作任何集合类型的实用方法,例如,下面是一个检测任意集合是否包含指定元素的泛型方法:

    public static <E> boolean contains(Collection<E> c, Object obj) {

        for (E elenent : c)

            if (elenent.equals(obj))

                return true;

        return false;

    }

    Java类库的设计者认为:这些实用方法中的某些方法非常有用,应该将它们提供给用户使用。这样,类库的使用者就不必自己重新构建这些方法了。contains就是这样一个实用方法。

    事实上,Collection接口声明了很多有用的方法,所有的实现类都必须提供这些方法。下面列举了其中的一部分:……

    在这些方法中,有许多方法的功能非常明确,不需要过多的解释。在本节尾部的API注释中可以找到有关它们的完整文档说明。

    当然,如果实现Collection接口的每一个类都要提供如此多的例行方法将是一件很烦人的事情。为了能够让实现者更容易地实现这个接口,Java类库提供了一个类AbstractCollection。它将基础方法sizeiterator抽象化了,但是在此提供了例行方法。例如:

public abstract class AbstractCollection<E> implements Collection<E> {

    public abstract Iterator<E> iterator();

 

    public boolean contains(Object obj) {

        for (E elemeit : c)

            // calls iterator()

            if (element.equals(ob))

                return true;

        return false;

    }

}

    此时,一个具体的集合类可以扩展AbstractCollection类了,现在要由具体的集合类提供iterator方法,而contains方法已由AbstractCollection超类提供了。然而,如果子类有更加有效的方式实现contains方法,也可以由子类提供,就这点而言,没有什么限制。

    对于类框架来说,这是一个很好的设计。集合类的用户可以使用泛型接口中一组更加丰富的方法,而实际的数据结构实现者并没有需要实现所有例行方法的负担。

APIjava.util.Collection<E> 1.2

  • Iterator<E> iterator():返回一个用于访问集合中每个元素的迭代器。

  • int size():返回当前存储在集合中的元素个数。

  • boolean isEmpty():如果集合中没有元素,返回true。

  • boolean contains(Object obj):如果集合中包含了一个与obj相等的对象,返回true。

  • booTean containsAll(Collection<?> other):如果这个集合包含other集合中的所有元素,返回true。

  • boolean add(Object element):将一个元素添加到集合中。如果由于这个调用改变了集合,返回true。

  • boolean addAll(Collection<? extends E> other):将other集合中的所有元素添加到这个集合。如果由于这个调用改变了集合,返回true。

  • boolean remove(Object obj):从这个集合中删除等于obj的对象。如果有匹配的对象被删除,返回true。

  • boolean removeAll(Collectlon<?> other):从这个集合中删除other集合中存在的所有元素。如果由于这个调用改变了集合,返回true。

  • void clear():从这个集合中删除所有的元素。

  • boolean retainAll(Collection<?> other):从这个集合中删除所有与other集合中的元素不同的元素。如果由于这个调用改变了集合,返回true。

  • Object[] toArray():返回这个集合的对象数组。

  • <T> T[] toArray(T[] arrayToFill):返回这个集合的对象数组。如果arrayToFill足够大,就将集合中的元素填入这个数组中,剩余空间填补null;否则,分配一个新数组,其成员类型与arrayToFill的成员类型相同,其长度等于集合的大小,并添入集合元素。

APIjava.util.Iterator<E> 1.2

  • boolean hasNext():如果存在可坊问的元素,返回加true。

  • E next():返回将要访问的下一个对象。如果已经到达了集合的尾部,将抛出一个NoSuchElementException。

  • void remove():删除上次访问的对象。这个方法必须紧跟在访问一个元素之后执行。如果上次访问之后,集合已经发生了变化,这个方法将抛出一个IllegalStateException。


【总结】Java核心技术36讲知识点大纲 前段时间在极客时间上购买了杨晓峰老师的《Java核心技术36讲》,趁着这段时间有空,对相关知识点做了一个整体的目录,也对自己所掌握的Java基础进行了一个复习和梳理,若想深入学习,可以购买相关专栏,也可以对着该知识点查阅相关资料,源码等自学。—-kayfen 文末附专栏链接 Java核心技术36讲知识点总结 1 Java平台的理解 Java的特性,解释运行和编译运行 2 E... 阅读详情

相关推荐

Java核心技术(原书8)I_基础知识 完整pdf

Java核心技术(原书8)I_基础知识 完整pdf 中文影印,比较清楚,因为只能上传48M的文件,故分2下载,这是第一部分

集合1集合接口

目录 一、将集合接口实现分离 二、Java类库中的集合接口和迭代器接口 1、迭代器 2、删除元素 3、泛型实用方法 三、Java集合框架简介 1、Collection 2、Map 说明:这一系列博客内容摘自《Java核心技术 I》 一、将集合接口实现分离 与现代的数据结构类库的常见情况一样,Java集合类库也将接口实现分离。首先,看一下人们熟悉的数据结构——队列...

wyplj2015的博客 432

Java核心技术(原书8)I_基础知识简要摘要(一)

Java核心技术(原书8)I_基础知识简要摘要(一) 1.javac程序是一个Java编译器。它将文件Welcom.java编译成Welcom.class,并发送到Java虚拟机。虚拟机执行编译器存放在class文件中的字节码。 2.类是构建所有Java应用程序和applet的构建块。Java应用程序中的全部内容都必须放置在类中。 3.关键字class后面紧跟类名。Java中定义类名的规则很...

daixuhui845270247的博客 348

集合(一)

.集合接口 ★Enumeration接口提供了一种用于访问任意容器中各个元素的抽象机制。 1.集合接口实现分离 (1)队列接口指出可以在队列的尾部添加元素,在队列的头部删除元素,并且可以查找队列中元素的个数。当需要收集对象,并按照“先进先出”的规则检索对象时就应该使用队列(2)队列通常有两种实现方式: ◆使用循环数使用链表 (3)如果需要一个循环数队列,就可以用A

huatek_wp的博客 499

Java核心技术 1 基础知识

网站 更多书籍点击进入>> CiCi岛 下载 电子仅供预览及学习交流使用,下载后请24小时内删除,支持正,喜欢的请购买正书籍 电子书下载(皮皮云盘-点击“普通下载”) 购买正 封页 编辑推荐 Java领域*有影响力和价值的著作之一,与《Java编程思想》齐名,10余年全球畅销不衰,广受好评   根据Java SE 8全面更新,系统全面讲解Java语言的核心概念、语法...

CiCi岛的专栏 2万+

Java核心技术I:基础知识(原书8):13.3 集合框架

铁文整理 13.3 集合框架     框架是一个类的集,它奠定了创建高级功能的基础。框架包含很多超类,这些超类拥有非常有用的功能、策略和机制。框架使用者创建的子类可以扩展超类的功能,而不必重新创建这些基本的机制。例如,Swing就是一种用户界面的机制。     Java集合类库构成了集合类的框架。它为集合实现者定义了大量的接口和抽象类(见图13-10),并且对其中的某些机制给予了描

tiewen的专栏 914

Java核心技术(原书10) Ⅰ》阅读心得与总结

放假前打算花利用假期时间阅读一下《Java核心技术 Ⅰ》(以下简称“Ⅰ”),终于花了一个月的时间粗细兼并地读完了这本书。今天抽点儿时间写个博客,总结一下阅读成果好了。 这本书给人最大的感受是知识讲得很专业,比本科教材内容充实几倍,豆瓣目前评分7.6,褒贬不一。先说说缺点吧,个人认为阅读体验并不是很好。主要体现在: 基础知识部分介绍很详细,几乎无死角,内容跟得上时代,但正因为这样,有些地方实在难以理解; 各章节间耦合性较低,知识没有问题,但逻辑安排似乎有点儿问题,不懂作者为何这样安排...

wenjingyu的专栏 851

Java核心技术1基础知识原书10

本书为专业程序员解决实际问题而写,Java基础知识面覆盖很完整,可以帮助你深入了解Java语言和库。在I中,Horstmann主要强调基本语言概念和现代用户界面编程基础,深入介绍了从Java面向对象编程到泛型、集合、lambda表达式、Swing UI设计以及并发和函数式编程的新方法等内容。 目录 译者序 前言 致谢 第1章 Java程序设计概述 1 第2章 Java程序设计环境 12 第3章 Java的基本程序设计结构 28 第4章 对象与类 91 第5章 继承 147 第6章 接口、lambd

持 开源思想,撰 必胜所学,望 勤学者,无难处 4171

Java 核心技术 1》 笔记 第13集合(2)集合接口和常用方法介绍

集合类的常用方法有哪些?迭代器怎么用?自己定义的集合如何实现forEach遍历?

yu_duan_hun的博客 940

Java核心技术I:基础知识(原书8):13.2 具体的集合

铁文整理 13.2 具体的集合     这里并不打算更加详细地介绍所有的接口,但我们认为先介绍一下Java类库提供的具体数据结构还是很有用途的。透彻地介绍了人们想使用的类之后,再回过头研究一些抽象的概念,看一看集合框架织这些类的方式,表13-1展示了Java类库中的集合,并简要描述了每个集合类的用途(鉴于简单起见,省略了将在第十四章中介绍的线程安全集合)。在表13-1中,除了以Map

tiewen的专栏 1783

Core Java最新 Ⅱ 上市!快来看看有哪些重要更新!

Core Java最新 Ⅱ 上市!快来看看有哪些重要更新!

小虚竹的专栏 1万+

Java核心技术 I:基础知识原书8)》勘误表

limuyuan2008的专栏 954

Java核心技术I:基础知识(原书8):13.4 算法

铁文整理 13.4 算法     泛型集合接口有一个很大的优点,即算法只需要实现—次。例如,考虑一下计算集合中最大元素这样一个简单的算法。使用传统方式,程序设计人员可能会用循环实现这个算法。下面就是找出数中最大元素的代码。     ……     当然,为找出数列表中的最大元素所编写的代码会与此有微小的差别。     ……     链表应该怎么做呢?对于链表来说,无法实施高效

tiewen的专栏 768

Java 核心技术 1PDF

Java 核心技术解析:面向对象编程与设计模式 Java一种广泛使用的编程语言,其核心技术的掌握对于软件开发尤为重要。《Java 核心技术 1》深入探讨了 Java基础知识,包括面向对象编程、异常处理、泛型等。本篇文章将围绕这些概念,结合代码示例进行科普,旨在帮助读者更好地理解 Java核心技术。 一、面向对...

weixin_35814757的博客 705

Java 核心技术 1》 笔记 第13集合8)视图()

同步视图、被检验视图与数集合的相互转换

yu_duan_hun的博客 793

Java核心技术I:基础知识(原书8):13.5 遗留的集合

铁文整理 13.5 遗留的集合     本节将讨论Java程序设计语言自问世以来就存在的集合类;Hashtable类和非常有用的子类Properties、Vector的子类Stack以及BitSet类。 13.5.1 Hashtable类     Hashtable类与HashMap类的作用一样,实际上,它们拥有相同的接口。与Vector类的方法一样。Hashtable的方法也是同

tiewen的专栏 947

Java 核心技术 1》 笔记 第13集合(3)列表

常用集合介绍与链式列表、数列表

yu_duan_hun的博客 1328

哇哦!《Java核心技术》都更到12了,牛逼!

Java 已经诞生 27 年,在全球范围内拥有超过 1200 万的开发者群体,有超过 450 亿物理设备使用Java 技术,云端数据中心的虚拟化环境里,还运行着超过 250 亿个 Java 虚拟机的进程实例……这些数据都是 Java 过去取得巨大成就的佐证。进入云原生时代以后,传统的 Java 技术正遭受诸多挑战,“一次编写,到处运行”等植根于 Java 基因里的前提...

公众号-老炮说Java 8335

Java核心技术I:基础知识(原书8):14.7 线程安全的集合

铁文整理 14.7 线程安全的集合     如果多线程要并发地修改一个数据结构,例如散列表,那么很容易会破坏这个数据结构(有关散列表的详细信息见第13章)。例如,一个线程可能要开始向表中插入一个新元素。假定在调整散列表各个桶之间的链接关系的过程中,被剥夺了控制权。如果一个线程也开始遍历同一个链表,可能使用无效的链接并造成混乱,会抛出异常或者陷入死循环。     可以通过提供锁来保护

tiewen的专栏 1119
上一篇: Java核心技术卷I:基础知识(原书第8版):12.9 反射和泛型
下一篇: Java核心技术卷I:基础知识(原书第8版):13.3 集合框架
铁文
博客等级 码龄22年 441粉丝 148原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值