十二、集合(5)

本章概要

  • for-in 和迭代器
    • 适配器方法惯用法
  • 本章小结
    • 简单集合分类

for-in和迭代器

到目前为止,for-in 语法主要用于数组,但它也适用于任何 Collection 对象。实际上在使用 ArrayList 时,已经看到了一些使用它的示例,下面是它的通用性的证明:

import java.util.*;public class ForInCollections {public static void main(String[] args) {Collection<String> cs = new LinkedList<>();Collections.addAll(cs,"Take the long way home".split(" "));for (String s : cs) {System.out.print("'" + s + "' ");}}
}

在这里插入图片描述

由于 cs 是一个 Collection ,因此该代码展示了使用 for-in 是所有 Collection 对象的特征。

这样做的原因是 Java 5 引入了一个名为 Iterable 的接口,该接口包含一个能够生成 Iteratoriterator() 方法。for-in 使用此 Iterable 接口来遍历序列。因此,如果创建了任何实现了 Iterable 的类,都可以将它用于 for-in 语句中:

import java.util.*;public class IterableClass implements Iterable<String> {protected String[] words = ("And that is how we know the Earth to be banana-shaped.").split(" ");@Overridepublic Iterator<String> iterator() {return new Iterator<String>() {private int index = 0;@Overridepublic boolean hasNext() {return index < words.length;}@Overridepublic String next() {return words[index++];}@Overridepublic void remove() { // Not implementedthrow new UnsupportedOperationException();}};}public static void main(String[] args) {for (String s : new IterableClass()) {System.out.print(s + " ");}}
}

在这里插入图片描述

iterator() 返回的是实现了 Iterator 的匿名内部类的实例,该匿名内部类可以遍历数组中的每个单词。在主方法中,可以看到 IterableClass 确实可以用于 for-in 语句。

在 Java 5 中,许多类都是 Iterable ,主要包括所有的 Collection 类(但不包括各种 Maps )。 例如,下面的代码可以显示所有的操作系统环境变量:

import java.util.*;public class EnvironmentVariables {public static void main(String[] args) {for (Map.Entry entry : System.getenv().entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue());}}
}

在这里插入图片描述

System.getenv() 返回一个 MapentrySet() 产生一个由 Map.Entry 的元素构成的 Set ,并且这个 Set 是一个 Iterable ,因此它可以用于 for-in 循环。

for-in 语句适用于数组或者其它任何 Iterable ,但这并不代表数组一定是 Iterable ,也不会发生任何自动装箱:

import java.util.*;public class ArrayIsNotIterable {static <T> void test(Iterable<T> ib) {for (T t : ib) {System.out.print(t + " ");}}public static void main(String[] args) {test(Arrays.asList(1, 2, 3));String[] strings = {"A", "B", "C"};// An array works in for-in, but it's not Iterable://- test(strings);// You must explicitly convert it to an Iterable:test(Arrays.asList(strings));}
}

在这里插入图片描述

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

适配器方法惯用法

如果现在有一个 Iterable 类,你想要添加一种或多种在 for-in 语句中使用这个类的方法,应该怎么做呢?例如,你希望可以选择正向还是反向遍历一个单词列表。如果直接继承这个类,并重写 iterator() 方法,则只能替换现有的方法,而不能实现遍历顺序的选择。

一种解决方案是所谓_适配器方法_(Adapter Method)的惯用法。“适配器”部分来自于设计模式,因为必须要提供特定的接口来满足 for-in 语句。如果已经有一个接口并且需要另一个接口时,则编写适配器就可以解决这个问题。
在这里,若希望在默认的正向迭代器的基础上,添加产生反向迭代器的能力,因此不能使用重写,相反,而是添加了一个能够生成 Iterable 对象的方法,该对象可以用于 for-in 语句。这使得我们可以提供多种使用 for-in 语句的方式:

import java.util.*;class ReversibleArrayList<T> extends ArrayList<T> {ReversibleArrayList(Collection<T> c) {super(c);}public Iterable<T> reversed() {return new Iterable<T>() {@Overridepublic Iterator<T> iterator() {return new Iterator<T>() {int current = size() - 1;@Overridepublic boolean hasNext() {return current > -1;}@Overridepublic T next() {return get(current--);}@Overridepublic void remove() { // Not implementedthrow new UnsupportedOperationException();}};}};}
}public class AdapterMethodIdiom {public static void main(String[] args) {ReversibleArrayList<String> ral =new ReversibleArrayList<String>(Arrays.asList("To be or not to be".split(" ")));// Grabs the ordinary iterator via iterator():for (String s : ral) {System.out.print(s + " ");}System.out.println();// Hand it the Iterable of your choicefor (String s : ral.reversed()) {System.out.print(s + " ");}}
}

在这里插入图片描述

在主方法中,如果直接将 ral 对象放在 for-in 语句中,则会得到(默认的)正向迭代器。但是如果在该对象上调用 reversed() 方法,它会产生不同的行为。

通过使用这种方式,可以在 IterableClass.java 示例中添加两种适配器方法:

MultiIterableClass.java

import java.util.*;public class MultiIterableClass extends IterableClass {public Iterable<String> reversed() {return new Iterable<String>() {@Overridepublic Iterator<String> iterator() {return new Iterator<String>() {int current = words.length - 1;@Overridepublic boolean hasNext() {return current > -1;}@Overridepublic String next() {return words[current--];}@Overridepublic void remove() { // Not implementedthrow new UnsupportedOperationException();}};}};}public Iterable<String> randomized() {return new Iterable<String>() {@Overridepublic Iterator<String> iterator() {List<String> shuffled =new ArrayList<String>(Arrays.asList(words));Collections.shuffle(shuffled, new Random(47));return shuffled.iterator();}};}public static void main(String[] args) {MultiIterableClass mic = new MultiIterableClass();for (String s : mic.reversed()) {System.out.print(s + " ");}System.out.println();for (String s : mic.randomized()) {System.out.print(s + " ");}System.out.println();for (String s : mic) {System.out.print(s + " ");}}
}

IterableClass.java

import java.util.Iterator;public class IterableClass implements Iterable<String> {protected String[] words = ("And that is how we know the Earth to be banana-shaped.").split(" ");@Overridepublic Iterator<String> iterator() {return new Iterator<String>() {private int index = 0;@Overridepublic boolean hasNext() {return index < words.length;}@Overridepublic String next() {return words[index++];}@Overridepublic void remove() { // Not implementedthrow new UnsupportedOperationException();}};}public static void main(String[] args) {for (String s : new IterableClass()) {System.out.print(s + " ");}}
}

在这里插入图片描述

注意,第二个方法 random() 没有创建它自己的 Iterator ,而是直接返回被打乱的 List 中的 Iterator

从输出中可以看到, Collections.shuffle() 方法不会影响到原始数组,而只是打乱了 shuffled 中的引用。之所以这样,是因为 randomized() 方法用一个 ArrayListArrays.asList() 的结果包装了起来。如果这个由 Arrays.asList() 生成的 List 被直接打乱,那么它将修改底层数组,如下所示:

import java.util.*;public class ModifyingArraysAsList {public static void main(String[] args) {Random rand = new Random(47);Integer[] ia = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};List<Integer> list1 =new ArrayList<>(Arrays.asList(ia));System.out.println("Before shuffling: " + list1);Collections.shuffle(list1, rand);System.out.println("After shuffling: " + list1);System.out.println("array: " + Arrays.toString(ia));List<Integer> list2 = Arrays.asList(ia);System.out.println("Before shuffling: " + list2);Collections.shuffle(list2, rand);System.out.println("After shuffling: " + list2);System.out.println("array: " + Arrays.toString(ia));}
}

在这里插入图片描述

在第一种情况下, Arrays.asList() 的输出被传递给了 ArrayList 的构造器,这将创建一个引用 ia 的元素的 ArrayList ,因此打乱这些引用不会修改该数组。但是,如果直接使用 Arrays.asList(ia) 的结果,这种打乱就会修改 ia 的顺序。重要的是要注意 Arrays.asList() 生成一个 List 对象,该对象使用底层数组作为其物理实现。如果对 List 对象做了任何修改,又不想让原始数组被修改,那么就应该在另一个集合中创建一个副本。

本章小结

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

  1. 数组将数字索引与对象相关联。它保存类型明确的对象,因此在查找对象时不必对结果做类型转换。它可以是多维的,可以保存基本类型的数据。虽然可以在运行时创建数组,但是一旦创建数组,就无法更改数组的大小。
  2. Collection 保存单一的元素,而 Map 包含相关联的键值对。使用 Java 泛型,可以指定集合中保存的对象的类型,因此不能将错误类型的对象放入集合中,并且在从集合中获取元素时,不必进行类型转换。各种 Collection 和各种 Map 都可以在你向其中添加更多的元素时,自动调整其尺寸大小。集合不能保存基本类型,但自动装箱机制会负责执行基本类型和集合中保存的包装类型之间的双向转换。
  3. 像数组一样, List 也将数字索引与对象相关联,因此,数组和 List 都是有序集合。
  4. 如果要执行大量的随机访问,则使用 ArrayList ,如果要经常从表中间插入或删除元素,则应该使用 LinkedList
  5. 队列和堆栈的行为是通过 LinkedList 提供的。
  6. Map 是一种将对象(而非数字)与对象相关联的设计。 HashMap 专为快速访问而设计,而 TreeMap 保持键始终处于排序状态,所以没有 HashMap 快。 LinkedHashMap 按插入顺序保存其元素,但使用散列提供快速访问的能力。
  7. Set 不接受重复元素。 HashSet 提供最快的查询速度,而 TreeSet 保持元素处于排序状态。 LinkedHashSet 按插入顺序保存其元素,但使用散列提供快速访问的能力。
  8. 不要在新代码中使用遗留类 VectorHashtableStack

浏览一下Java集合的简图(不包含抽象类或遗留组件)会很有帮助。这里仅包括在一般情况下会碰到的接口和类。(译者注:下图为原著PDF中的截图,可能由于未知原因存在问题。这里可参考译者绘制版)

在这里插入图片描述

简单集合分类

可以看到,实际上只有四个基本的集合组件: MapListSetQueue ,它们各有两到三个实现版本(Queuejava.util.concurrent 实现未包含在此图中)。最常使用的集合用黑色粗线线框表示。

虚线框表示接口,实线框表示普通的(具体的)类。带有空心箭头的虚线表示特定的类实现了一个接口。实心箭头表示某个类可以生成箭头指向的类的对象。例如,任何 Collection 都可以生成 IteratorList 可以生成 ListIterator (也能生成普通的 Iterator ,因为 List 继承自 Collection )。

下面的示例展示了各种不同的类在方法上的差异。实际代码来自泛型章节,在这里只是调用它来产生输出。程序的输出还展示了在每个类或接口中所实现的接口:

CollectionDifferences.java

public class CollectionDifferences {public static void main(String[] args) {CollectionMethodDifferences.main(args);}
}

CollectionMethodDifferences.java

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;public class CollectionMethodDifferences {static Set<String> methodSet(Class<?> type) {return Arrays.stream(type.getMethods()).map(Method::getName).collect(Collectors.toCollection(TreeSet::new));}static void interfaces(Class<?> type) {System.out.print("Interfaces in " + type.getSimpleName() + ": ");System.out.println(Arrays.stream(type.getInterfaces()).map(Class::getSimpleName).collect(Collectors.toList()));}static Set<String> object = methodSet(Object.class);static {object.add("clone");}static voiddifference(Class<?> superset, Class<?> subset) {System.out.print(superset.getSimpleName() +" extends " + subset.getSimpleName() +", adds: ");Set<String> comp = Sets.difference(methodSet(superset), methodSet(subset));comp.removeAll(object); // Ignore 'Object' methodsSystem.out.println(comp);interfaces(superset);}public static void main(String[] args) {System.out.println("Collection: " +methodSet(Collection.class));interfaces(Collection.class);difference(Set.class, Collection.class);difference(HashSet.class, Set.class);difference(LinkedHashSet.class, HashSet.class);difference(TreeSet.class, Set.class);difference(List.class, Collection.class);difference(ArrayList.class, List.class);difference(LinkedList.class, List.class);difference(Queue.class, Collection.class);difference(PriorityQueue.class, Queue.class);System.out.println("Map: " + methodSet(Map.class));difference(HashMap.class, Map.class);difference(LinkedHashMap.class, HashMap.class);difference(SortedMap.class, Map.class);difference(TreeMap.class, Map.class);}
}

Sets.java

import java.util.HashSet;
import java.util.Set;public class Sets {public static <T> Set<T> union(Set<T> a, Set<T> b) {Set<T> result = new HashSet<>(a);result.addAll(b);return result;}public static <T>Set<T> intersection(Set<T> a, Set<T> b) {Set<T> result = new HashSet<>(a);result.retainAll(b);return result;}// Subtract subset from superset:public static <T> Set<T>difference(Set<T> superset, Set<T> subset) {Set<T> result = new HashSet<>(superset);result.removeAll(subset);return result;}// Reflexive--everything not in the intersection:public static <T> Set<T> complement(Set<T> a, Set<T> b) {return difference(union(a, b), intersection(a, b));}
}

在这里插入图片描述

TreeSet 之外的所有 Set 都具有与 Collection 完全相同的接口。ListCollection 存在着明显的不同,尽管 List 所要求的方法都在 Collection 中。另一方面,在 Queue 接口中的方法是独立的,在创建具有 Queue 功能的实现时,不需要使用 Collection 方法。最后, MapCollection 之间唯一的交集是 Map 可以使用 entrySet()values() 方法来产生 Collection

请注意,标记接口 java.util.RandomAccess 附加到了 ArrayList 上,但不附加到 LinkedList 上。这为根据特定 List 动态改变其行为的算法提供了信息。

从面向对象的继承层次结构来看,这种组织结构确实有些奇怪。但是,当了解了 java.util 中更多的有关集合的内容后,就会发现除了继承结构有点奇怪外,还有更多的问题。集合类库一直以来都是设计难题——解决这些问题涉及到要去满足经常彼此之间互为牵制的各方面需求。所以要做好准备,在各处做出妥协。

尽管存在这些问题,但 Java 集合仍是在日常工作中使用的基本工具,它可以使程序更简洁、更强大、更有效。

下面是译者绘制的 Java 集合框架简图,黄色为接口,绿色为抽象类,蓝色为具体类。虚线箭头表示实现关系,实线箭头表示继承关系。

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

el-form表单中不同数据类型对应的时间格式化和校验规则

1. 在表单中, 当选择不同的数据类型时, 需要在下面选择时间时和数据类型对应上, 通过监听数据类型的变化, 给时间做格式化, 2. 但是当不按顺序选择数据类型后, 再选时间可能会报错, 所以需要在dom更新后, 再清空表单. 3. 校验规则, 结束时间需要大于开始时间, 但是不能选当前的…

排序算法:快速排序(三种排序方式、递归和非递归)

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关排序算法的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通…

GeoNet: Unsupervised Learning of Dense Depth, Optical Flow and Camera Pose 论文阅读

论文信息 题目&#xff1a;GeoNet: Unsupervised Learning of Dense Depth, Optical Flow and Camera Pose 作者&#xff1a;Zhichao Yin and Jianping Shi 来源&#xff1a;CVPR 时间&#xff1a;2018 Abstract 我们提出了 GeoNet&#xff0c;这是一种联合无监督学习框架&a…

vim常用操作

一、Esc键 & 命令模式 1.撤销&#xff1a;u 恢复撤销&#xff1a;Ctrl r 2.定位 行首&#xff1a;0 行尾&#xff1a;$ 第7行&#xff1a;7G 3.编辑 下行开始插入&#xff1a; o 删除行&#xff1a;dd 复制3行并粘贴&#xff1a;3yy ---> p 复制单词并粘贴&#…

蓝桥杯官网练习题(玩具蛇)

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 小蓝有一条玩具蛇&#xff0c;一共有 16 节&#xff0c;上面标着数字 1 至 16。每一节都是一个正方形的形状。相邻的两节可以成直线或者成 90 度角。 小蓝还有一个…

时序预测 | MATLAB实现ELM极限学习机时间序列预测未来

时序预测 | MATLAB实现ELM极限学习机时间序列预测未来 目录 时序预测 | MATLAB实现ELM极限学习机时间序列预测未来预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.MATLAB实现ELM极限学习机时间序列预测未来&#xff1b; 2.运行环境Matlab2018及以上&#xff0c;data为数…

【漏洞复现】H3C路由器信息泄露任意用户登录

漏洞描述 通过访问特地址得到密码可进行登录。 免责声明 技术文章仅供参考&#xff0c;任何个人和组织使用网络应当遵守宪法法律&#xff0c;遵守公共秩序&#xff0c;尊重社会公德&#xff0c;不得利用网络从事危害国家安全、荣誉和利益&#xff0c;未经授权请勿利用文章中…

CASAIM与南京航空航天大学在自动化叶片曲面分析系统开展合作,推动航空航天发动机零部件自动化3D检测进程

近期&#xff0c;CASAIM与南京航空航天大学在自动化叶片曲面分析系统展开深入合作&#xff0c;充分发挥双方在航空航天和智能检测领域优势&#xff0c;共同推动航空航天发动机零部件自动化3D检测进程。 南京航空航天大学创建于1952年10月&#xff0c;是新中国自己创办的第一批…

pyCharm远程DEBUG

第一步&#xff0c;添加一个远程机器的解释器 ssh 远程机器解释器添加&#xff0c; 我本地ssh有配置目标机器。 如果没配置&#xff0c;那就选着new server configuration 新增一个。 interpreter 指定远程机器python&#xff0c; &#xff08;机器上有多个版本python里尤其要…

红队打靶:ConnectTheDots打靶思路详解(vulnhub)

目录 写在开头 第一步&#xff1a;主机发现和端口扫描 第二步&#xff1a;FTP和NFS渗透&#xff08;失败&#xff09; 第三步&#xff1a;web渗透 第四步&#xff1a;jsfuck解码 第五步&#xff1a;再次FTP渗透与莫尔斯电码解码 第六步&#xff1a;vim读取断电swp文件…

批量采集的时间管理与优化

在进行大规模数据采集时&#xff0c;如何合理安排和管理爬取任务的时间成为了每个专业程序员需要面对的挑战。本文将分享一些关于批量采集中时间管理和优化方面的实用技巧&#xff0c;帮助你提升爬虫工作效率。 1. 制定明确目标并设置合适频率 首先要明确自己所需获取数据的范…

Bazzite 发行版 1.0 发布,可让 Linux 游戏机实现 Steam Deck 桌面环境体验

导读近日消息&#xff0c;当下 Steam Deck 掌机的性能已经有所过时&#xff0c;不过许多玩家为了追求原生 SteamOS 体验依然选择购买该掌机&#xff0c;V社此前曾表示&#xff0c;“SteamOS 简化了在手持设备上玩 PC 游戏的过程”&#xff0c;玩家在用 Steam Deck 玩游戏时&…

BCSP-玄子Share-Java框基础_工厂模式/代理模式

三、设计模式 3.1 设计模式简介 软件设计中的三十六计是人们在长期的软件开发中的经验总结是对某些特定问题的经过实践检验的特定解决方法被广泛运用在 Java 框架技术中 3.1.1 设计模式的优点 设计模式是可复用的面向对象软件的基础可以更加简单方便地复用成功的设计和体系…

springcloud-Eureka

1.Eureka注册中心 1.1 简介与依赖导入 1.2 服务注册与发现 启动eureka模块 访问Eureka 将user-service,book-service,borrow-service作为eureka的客户端&#xff0c;先导包。三个导入方式一样。 配置文件&#xff0c;三个模块下都一样配置 然后分别启动三个模块 发现注册…

SpringMvc--CRUD

目录 一.什么是SpringMvc--CRUD 二.前期准备 公共页面跳转(专门用来处理页面跳转) 三.ssm之CRUD后端实现 配置pom.xml 双击mybatis-generator:generate自动生成mapper 编写generatorConfig.xml 项目结构 编写PagerAspect切面类 编写hpjyBiz接口类 编写hpjyBizImpl接…

JavaWeb_LeadNews_Day11-KafkaStream实现实时计算文章分数

JavaWeb_LeadNews_Day11-KafkaStream实现实时计算文章分数 KafkaStream概述案例-统计单词个数SpringBoot集成 实时计算文章分值来源Gitee KafkaStream 概述 Kafka Stream: 提供了对存储与Kafka内的数据进行流式处理和分析的功能特点: Kafka Stream提供了一个非常简单而轻量的…

Pytorch 多卡并行(1)—— 原理简介和 DDP 并行实践

近年来&#xff0c;深度学习模型的规模越来越大&#xff0c;需要处理的数据也越来越多&#xff0c;单卡训练的显存空间和计算效率都越来越难以满足需求。因此&#xff0c;多卡并行训练成为了一个必要的解决方案本文主要介绍使用 Pytorch 的 DistributedDataParallel&#xff08…

合宙Air724UG LuatOS-Air LVGL API控件-表格(Table)

表格&#xff08;Table&#xff09; 示例代码 --创建表格Table1 lvgl.table_create(lvgl.scr_act(),nil)--设置表格为4行5列lvgl.table_set_row_cnt(Table1,4)lvgl.table_set_col_cnt(Table1,5)--给每个单元格赋值lvgl.table_set_cell_value(Table1, 0, 0, "选手")l…

Android之RecyclerView仿ViewPage滑动

文章目录 前言一、效果图二、实现步骤1.xml主布局2.所有用到的drawable资源文件3.xml item布局4.adapter适配器5.javabean实体类6.activity使用 总结 前言 我们都知道ViewPageFragment滑动&#xff0c;但是的需求里面已经有了这玩意&#xff0c;但是在Fragment中还要有类似功能…

基于3D扫描和3D打印的产品逆向工程实战【数字仪表】

逆向工程是一种从物理零件创建数字设计的强大方法&#xff0c;并且可以与 3D 扫描和 3D 打印等技术一起成为原型设计工具包中的宝贵工具。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 3D 扫描仪可以非常快速地测量复杂的物体&#xff0c;并且在涉及现实生活参考时可以…