十二、集合(1)

news2024/11/24 0:57:21

本章概要

  • 泛型和类型安全的集合
  • 基本概念

如果一个程序只包含固定数量的对象且对象的生命周期都是已知的,那么这是一个非常简单的程序。

通常,程序总是根据运行时才知道的某些条件去创建新的对象。在此之前,无法知道所需对象的数量甚至确切类型。为了解决这个普遍的编程问题,需要在任意时刻和任意位置创建任意数量的对象。因此,不能依靠创建命名的引用来持有每一个对象:

MyType aReference;

因为从来不会知道实际需要多少个这样的引用。

大多数编程语言都提供了某种方法来解决这个基本问题。Java有多种方式保存对象(确切地说,是对象的引用)。例如前边曾经学习过的数组,它是编译器支持的类型。数组是保存一组对象的最有效的方式,如果想要保存一组基本类型数据,也推荐使用数组。但是数组具有固定的大小尺寸,而且在更一般的情况下,在写程序的时候并不知道将需要多少个对象,或者是否需要更复杂的方式来存储对象,因此数组尺寸固定这一限制就显得太过受限了。

java.util 库提供了一套相当完整的_集合类_(collection classes)来解决这个问题,其中基本的类型有 ListSetQueueMap。这些类型也被称作_容器类_(container classes),但我将使用Java类库使用的术语。集合提供了完善的方法来保存对象,可以使用这些工具来解决大量的问题。

集合还有一些其它特性。例如, Set 对于每个值都只保存一个对象, Map 是一个关联数组,允许将某些对象与其他对象关联起来。Java集合类都可以自动地调整自己的大小。因此,与数组不同,在编程时,可以将任意数量的对象放置在集合中,而不用关心集合应该有多大。

尽管在 Java 中没有直接的关键字支持,但集合类仍然是可以显著增强编程能力的基本工具。在本章中,将介绍 Java 集合类库的基本知识,并重点介绍一些典型用法。这里将专注于在日常编程中使用的集合。稍后,在附录:集合主题中,还将学习到其余的那些集合和相关功能,以及如何使用它们的更多详细信息。

泛型和类型安全的集合

使用 Java 5 之前的集合的一个主要问题是编译器允许你向集合中插入不正确的类型。例如,考虑一个 Apple 对象的集合,这里使用最基本最可靠的 ArrayList 。现在,可以把 ArrayList 看作“可以自动扩充自身尺寸的数组”来看待。使用 ArrayList 相当简单:创建一个实例,用 add() 插入对象;然后用 get() 来访问这些对象,此时需要使用索引,就像数组那样,但是不需要方括号。 ArrayList 还有一个 size() 方法,来说明集合中包含了多少个元素,所以不会不小心因数组越界而引发错误。

在本例中, AppleOrange 都被放到了集合中,然后将它们取出。正常情况下,Java编译器会给出警告,因为这个示例没有使用泛型。在这里,使用特定的注解来抑制警告信息。注解以“@”符号开头,可以带参数。这里的 @SuppressWarning 注解及其参数表示只抑制“unchecked”类型的警告:

import java.util.*;

class Apple {
    private static long counter;
    private final long id = counter++;

    public long id() {
        return id;
    }
}

class Orange {
}

public class ApplesAndOrangesWithoutGenerics {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        ArrayList apples = new ArrayList();
        for (int i = 0; i < 3; i++) {
            apples.add(new Apple());
        }
        // No problem adding an Orange to apples:
        apples.add(new Orange());
        for (Object apple : apples) {
            ((Apple) apple).id();
            // Orange is detected only at run time
        }
    }
}

在这里插入图片描述

AppleOrange 是截然不同的,它们除了都是 Object 之外没有任何共同点(如果一个类没有显式地声明继承自哪个类,那么它就自动继承自 Object)。因为 ArrayList 保存的是 Object ,所以不仅可以通过 ArrayListadd() 方法将 Apple 对象放入这个集合,而且可以放入 Orange 对象,这无论在编译期还是运行时都不会有问题。当使用 ArrayListget() 方法来取出你认为是 Apple 的对象时,得到的只是 Object 引用,必须将其转型为 Apple。然后需要将整个表达式用括号括起来,以便在调用 Appleid() 方法之前,强制执行转型。否则,将会产生语法错误。

在运行时,当尝试将 Orange 对象转为 Apple 时,会出现输出中显示的错误。

在泛型章节中,你将了解到使用 Java 泛型来创建类可能很复杂。但是,使用预先定义的泛型类却相当简单。例如,要定义一个用于保存 Apple 对象的 ArrayList ,只需要使用 ArrayList 来代替 ArrayList 。尖括号括起来的是_类型参数_(可能会有多个),它指定了这个集合实例可以保存的类型。

通过使用泛型,就可以在编译期防止将错误类型的对象放置到集合中。下面还是这个示例,但是使用了泛型:

// collections/ApplesAndOrangesWithGenerics.java
import java.util.*;

public class ApplesAndOrangesWithGenerics {
    public static void main(String[] args) {
        ArrayList<Apple> apples = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            apples.add(new Apple());
        }
        // Compile-time error:
        // apples.add(new Orange());
        for (Apple apple : apples) {
            System.out.println(apple.id());
        }
    }
}
0
1
2

apples 定义的右侧,可以看到 new ArrayList<>() 。这有时被称为“菱形语法”(diamond syntax)。在 Java 7 之前,必须要在两端都进行类型声明,如下所示:

ArrayList<Apple> apples = new ArrayList<Apple>();

随着类型变得越来越复杂,这种重复产生的代码非常混乱且难以阅读。程序员发现所有类型信息都可以从左侧获得,因此,编译器没有理由强迫右侧再重复这些。虽然_类型推断_(type inference)只是个很小的请求,Java 语言团队仍然欣然接受并进行了改进。

有了 ArrayList 声明中的类型指定,编译器会阻止将 Orange 放入 apples ,因此,这会成为一个编译期错误而不是运行时错误。

使用泛型,从 List 中获取元素不需要强制类型转换。因为 List 知道它持有什么类型,因此当调用 get() 时,它会替你执行转型。因此,使用泛型,你不仅知道编译器将检查放入集合的对象类型,而且在使用集合中的对象时也可以获得更清晰的语法。

当指定了某个类型为泛型参数时,并不仅限于只能将确切类型的对象放入集合中。向上转型也可以像作用于其他类型一样作用于泛型:

// collections/GenericsAndUpcasting.java
import java.util.*;

class GrannySmith extends Apple {
}

class Gala extends Apple {
}

class Fuji extends Apple {
}

class Braeburn extends Apple {
}

public class GenericsAndUpcasting {
    public static void main(String[] args) {
        ArrayList<Apple> apples = new ArrayList<>();
        apples.add(new GrannySmith());
        apples.add(new Gala());
        apples.add(new Fuji());
        apples.add(new Braeburn());
        for (Apple apple : apples) {
            System.out.println(apple);
        }
    }
}

在这里插入图片描述

因此,可以将 Apple 的子类型添加到被指定为保存 Apple 对象的集合中。

程序的输出是从 Object 默认的 toString() 方法产生的,该方法打印类名,后边跟着对象的散列码的无符号十六进制表示(这个散列码是通过 hashCode() 方法产生的)。

基本概念

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

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

尽管并非总是可行,但在理想情况下,你编写的大部分代码都在与这些接口打交道,并且唯一需要指定所使用的精确类型的地方就是在创建的时候。因此,可以像下面这样创建一个 List

List<Apple> apples = new ArrayList<>();

请注意, ArrayList 已经被向上转型为了 List ,这与之前示例中的处理方式正好相反。使用接口的目的是,如果想要改变具体实现,只需在创建时修改它就行了,就像下面这样:

List<Apple> apples = new LinkedList<>();

因此,应该创建一个具体类的对象,将其向上转型为对应的接口,然后在其余代码中都是用这个接口。

这种方式并非总是有效的,因为某些具体类有额外的功能。例如, LinkedList 具有 List 接口中未包含的额外方法,而 TreeMap 也具有在 Map 接口中未包含的方法。如果需要使用这些方法,就不能将它们向上转型为更通用的接口。

Collection 接口概括了_序列_的概念——一种存放一组对象的方式。下面是个简单的示例,用 Integer 对象填充了一个 Collection (这里用 ArrayList 表示),然后打印集合中的每个元素:

// collections/SimpleCollection.java
import java.util.*;

public class SimpleCollection {
    public static void main(String[] args) {
        Collection<Integer> c = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            c.add(i); // Autoboxing
        }
        for (Integer i : c) {
            System.out.print(i + ", ");
        }
    }
}
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,

这个例子仅使用了 Collection 中的方法(即 add() ),所以使用任何继承自 Collection 的类的对象都可以正常工作。但是 ArrayList 是最基本的序列类型。

add() 方法的名称就表明它是在 Collection 中添加一个新元素。但是,文档中非常详细地叙述到 add() “要确保这个 Collection 包含指定的元素。”这是因为考虑到了 Set 的含义,因为在 Set中,只有当元素不存在时才会添加元素。在使用 ArrayList ,或任何其他类型的 List 时,add() 总是表示“把它放进去”,因为 List 不关心是否存在重复元素。

可以使用 for-in 语法来遍历所有的 Collection ,就像这里所展示的那样。在本章的后续部分,还将学习到一个更灵活的概念,迭代器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/950128.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

热烈祝贺蜀益表面处理成功入选航天系统采购供应商库

经过航天系统采购平台的严审&#xff0c;眉山市蜀益表面处理科技有限公司成功入选中国航天系统采购供应商库。航天系统采购平台是航天系统内企业采购专用平台&#xff0c;服务航天全球范围千亿采购需求&#xff0c;目前&#xff0c;已有华为、三一重工、格力电器、科大讯飞等企…

Simulink建模与仿真(3)-Simulink 简介

分享一个系列&#xff0c;关于Simulink建模与仿真&#xff0c;尽量整理成体系 1、Simulink特点 Simulink是一个用来对动态系统进行建模、仿真和分析的软件包。使用Simulink来建模、分析和仿真各种动态系统(包括连续系统、离散系统和混合系统)&#xff0c;将是一件非常轻松的事…

natApp内网穿透工作原理

如图所示&#xff0c;用户启动内网穿透工具会将token传入natapp服务器与我们自己的主机建立一个类似于websocket的长链接&#xff0c;当从外网访问我们主机的接口时&#xff0c;会进行一个本地接口地址的截取&#xff0c;然后进行拼接成我们主机应用的真实地址。然后将数据返回…

AntDesign 自定义图片上传前端压缩画质

为什么压缩图片&#xff1f; 应为现在公司没有使用云数据库 从而为了减少服务器的消耗需要将用户上传的图片压缩 前端压缩图片的技术选择&#xff01; 查阅资料发现当下两种压缩的方法&#xff01;&#xff01;第一种使用工具库实现 npm install image-conversion --save 第…

JAVA集合汇总

数组&#xff1a; 固定长度&#xff0c;从栈中分配空间&#xff0c;对于程序方便快速&#xff0c;但是自由度小。 ​优点&#xff1a;下标定位&#xff0c;随机访问性强&#xff0c;查找速度快&#xff1b; ​缺点&#xff1a;插入、删除效率低&#xff0c;内存利用率低&…

前端最能打的本地存储方案

产品的原话就是“要又大又全”。既然存储量大&#xff0c;也要覆盖全多种设备多种浏览器。 方案选择 既然要存储的数量大&#xff0c;得排除cookielocalStorage&#xff0c;虽然比cookie多&#xff0c;但是同样有上限&#xff08;5M&#xff09;左右&#xff0c;备选websql 使…

SPSS--s04典型相关分析

典型相关基本原理 典型相关分析是主成分分析和因子分析的进一步发展 ,是研究两组变量间的相互依赖关系 ,把两组变量之间的相互关系变为研究两个新的变量之间的相关,而且又不抛弃原来变量的信息 ,这两个新的变量分别由第一组变量和第二组变量的线性组合构成 ,并且两组变量的个数…

以太网POE供电浪涌静电防护推荐TVS二极管

POE是一种传输技术&#xff0c;可在以太网电缆上传输电力和数据。1000M千兆以太网POE供电端口广泛用于安防、视频监控以及智能电网等工业系统&#xff0c;以实现系统内的数据、视频传输、流量控制、以及通过总线实现供电。由于工业以太网工作环境非常严酷苛刻&#xff0c;对于以…

Java不重启服务 扩展新支付功能demo

应用场景 对于支付&#xff1a;经常有些需求&#xff0c;需要我们扩展新的支付方式&#xff0c;但又不希望重启服务对于IOT&#xff1a;经常需要新增一些解析数据的Handler 那我们如何能做到不重启服务&#xff0c;就能扩展新功能呢&#xff1f;本文给一个简单的demo示例&…

无涯教程-Android - AutoCompleteTextView函数

AutoCompleteTextView是一个类似于EditText的视图&#xff0c;只是它在用户键入时自动显示补充数据。 AutoCompleteTextView - 属性 以下是与AutoCompleteTextView控件相关的重要属性。您可以查看Android官方文档以获取属性的完整列表以及可以在运行时更改这些属性的相关方法。…

循环冗余校验码(CRC校验)

CRC在线计算 CRC即循环冗余校验码&#xff08;Cyclic Redundancy Check&#xff09;&#xff1a;是数据通信领域中最常用的一种查错校验码&#xff0c;其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查&#xff08;CRC&#xff09;是一种数据传输检错功能&#xf…

会员管理系统实战开发教程06-会员充值

我们上篇讲解了会员开卡的操作&#xff0c;有了会员卡之后日常就是给会员进行充值&#xff0c;充值的逻辑是对余额进行累加&#xff0c;而且要记录充值的情况。 1 创建充值记录表 打开控制台&#xff0c;点击号创建数据源 输入数据源名称充值记录 点击编辑添加字段 先添加…

three.js(八):内置的三维几何体

三维几何体 BoxGeometry 立方体TetrahedronGeometry 四面体OctahedronGeometry 八面体DodecahedronGeometry 十二面体IcosahedronGeometry 二十面体PolyhedronGeometry 多面体SphereGeometry 球体ConeGeometry 圆锥CylinderGeometry 圆柱TorusGeometry 三维圆环TorusKnotGeomet…

部署你自己的导航站-dashy

现在每天要访问的网页都太多了&#xff0c;尽管chrome非常好用&#xff0c;有强大的标签系统。但是总觉的少了点什么。 今天我就来分享一个开源的导航网站系统 dashy。这是一个国外的大佬的开源项目 github地址如下&#xff1a;https://github.com/Lissy93/dashy 来简单说一下…

LabVIEW开发同步磁阻电机匝间短路故障在线诊断技术

LabVIEW开发同步磁阻电机匝间短路故障在线诊断技术 近年来&#xff0c;电动驾驶系统取得了实质性的改进&#xff0c;不仅在工业自动化和机器人技术中实施&#xff0c;而且在运输和风力发电中实施。自动化系统和其他执行器的典型要求包括&#xff1a;小尺寸、低重量、低成本、高…

【ES6】JavaScript 中的数组方法reduce

reduce() 是一个 JavaScript 中的数组方法&#xff0c;它会对数组的每个元素执行一个提供的 reducer 函数&#xff0c;将其减少到一个单一的值。 这是 reduce() 的基本用法&#xff1a; //(method) Array<number>.reduce(callbackfn: (previousValue: number, currentV…

如何在Windows / Mac / iPhone / Android / Online上将MP4转换为MP3

如果只想保留MP4视频的音频轨道&#xff0c;则可以将MP4转换为MP3格式。 MP3是几乎所有设备&#xff0c;播放器和编辑器都支持的数字音频格式。无论您将MP4视频转换为MP3音频以进行脱机播放或进一步编辑&#xff0c;都可以提取音轨并保存为MP3格式。这是在不损失质量的情况下将…

浅析Linux虚拟网络技术

文章目录 概述Tap/tun设备tun/tap的工作机制 Bridge网桥Bridge的工作机制Bridge IP 相关参考 概述 在传统的网络环境中&#xff0c;一台物理主机包含一张或多张网卡&#xff0c;要实现与其它物理主机之间的通信&#xff0c;需要将自身的网卡通过路由器或者交换机连接到外部的物…

解决gitee仓库中 .git 文件夹过大的问题

最近&#xff0c;许多项目都迁移到gitee。使用的也越来越频繁&#xff0c;但是今天突然收到一个仓库爆满的提示。让我一脸懵逼。本文将详细为你解答&#xff0c;这种情况如何处理。 1、起因 我收到的报错如下&#xff1a; remote: Powered by GITEE.COM [GNK-6.4] remote: T…

Docker - Docker安装MySql并启动

因为项目需要连接数据库&#xff0c;但是远程服务器上的mysql我不知道账户和密码&#xff0c;这个时候便是docker发挥作用的关键时刻了&#xff01; 目录 docker安装安装gcc卸载老docker&#xff08;如有&#xff09;安装软件包设置镜像仓库更新yum软件包索引安装docker启动doc…