十二、集合(5)

news2024/10/7 1:25:11

本章概要

  • 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(" ");

    @Override
    public Iterator<String> iterator() {
        return new Iterator<String>() {
            private int index = 0;

            @Override
            public boolean hasNext() {
                return index < words.length;
            }

            @Override
            public String next() {
                return words[index++];
            }

            @Override
            public void remove() { // Not implemented
                throw 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>() {
            @Override
            public Iterator<T> iterator() {
                return new Iterator<T>() {
                    int current = size() - 1;

                    @Override
                    public boolean hasNext() {
                        return current > -1;
                    }

                    @Override
                    public T next() {
                        return get(current--);
                    }

                    @Override
                    public void remove() { // Not implemented
                        throw 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 choice
        for (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>() {
            @Override
            public Iterator<String> iterator() {
                return new Iterator<String>() {
                    int current = words.length - 1;

                    @Override
                    public boolean hasNext() {
                        return current > -1;
                    }

                    @Override
                    public String next() {
                        return words[current--];
                    }

                    @Override
                    public void remove() { // Not implemented
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }

    public Iterable<String> randomized() {
        return new Iterable<String>() {
            @Override
            public 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(" ");

    @Override
    public Iterator<String> iterator() {
        return new Iterator<String>() {
            private int index = 0;

            @Override
            public boolean hasNext() {
                return index < words.length;
            }

            @Override
            public String next() {
                return words[index++];
            }

            @Override
            public void remove() { // Not implemented
                throw 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 void
    difference(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' methods
        System.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.coloradmin.cn/o/989289.html

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

相关文章

软件生命周期及流程【软件测试】

软件的生命周期 软件生命周期是软件开始研制到最终被废弃不用所经历的各个阶段。 瀑布型生命周期模型 规定了它们自上而下、相互衔接的固定次序&#xff0c;如同瀑布流水&#xff0c;逐级下落&#xff0c;具有顺序性和依赖性。每个阶段规定文档并需进行评审。 特点&#xff…

es滚动查询分析和使用步骤

ES在进行普通的查询时&#xff0c;默认只会查询出来10条数据。我们通过设置es中的size可以将最终的查询结果从10增加到10000。如果需要查询数据量大于es的翻页限制或者需要将es的数据进行导出又当如何&#xff1f; Elasticsearch提供了一种称为"滚动查询"&#xff08…

更换 yum 阿里源 - 手把手教你怎么配置,在也不需要求别人了 - 看懂一个就相当于看懂了其他的linux系统

更换阿里源 我的是centos8 当然 centos7 也可以换 后面有更详细的怎么配 &#xff0c;再也不用求别人怎么弄了 最直接的方式 直接复制 执行 centos7 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo或者 wget -O /etc/yum.repos.…

SpringMVC:从入门到精通,7篇系列篇带你全面掌握--三.使用SpringMVC完成增删改查

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于SpringMVC的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 效果演示 一.导入项目的相关依赖 二.…

规范预算编制,打造企业全面预算管理新章程

随着我国财税体系不断改革&#xff0c;经济形式日新月异&#xff0c;包括国有企业、事业单位在内的各类型企业对于财务会计和预算管理的要求越来越高。众所周知&#xff0c;传统的预算管理模式已经难以满足企业现代化、数字化进程的需求&#xff0c;面对横亘在高效工作面前的阻…

深度学习推荐系统(八)AFM模型及其在Criteo数据集上的应用

深度学习推荐系统(八)AFM模型及其在Criteo数据集上的应用 1 AFM模型原理及其实现 沿着特征工程自动化的思路&#xff0c;深度学习模型从 PNN ⼀路⾛来&#xff0c;经过了Wide&#xff06;Deep、Deep&#xff06;Cross、FNN、DeepFM、NFM等模型&#xff0c;进⾏了大量的、基于不…

【计组】3.5高速缓冲存储器

一、cache基本概念 二、cache—主存 映射方式 全相联映射&#xff08;随即放&#xff09; 主存块号、块内地址&#xff08;即记录cache块大小贮存块大小&#xff09; 有效位&#xff08;记录该cache块内是否转入主存信息&#xff09;、标记&#xff08;采用主存块号进行标记…

OpenVINO2023+Win 11配置

&#x1f482; 个人主页:风间琉璃&#x1f91f; 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 目录 前言 一、Anaconda 二、OpenVINO 三、PyCharm 前言 OpenVINO™是英特尔…

管理类联考——数学——汇总篇——知识点突破——数据分析——计数原理——排列组合——全能元素

⛲️ 一、考点讲解 1.全能元素特征 全能元素是指一个元素可以同时具备多个属性&#xff0c;在选取时&#xff0c;注意全能元素的归宿问题。 2.全能卡片 若一个卡片上的数字可以变化&#xff0c;则称为全能卡片&#xff0c;其解法是根据全能卡片是否选中来分类讨论。 二、考试解…

c++ 学习 之 类对象作为 类成员 ,构造函数和析构函数的先后顺序

前言 我们要学会用类对象作为 类成员&#xff0c;那让我们来深究一下构造函数和析构函数的先后顺序 正文 看代码 #define CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; // 来学习类对象作为类成员 // c类中的成员可以是另外一个类的对象&#xf…

开机启动应用

windows 建立快捷方式 winr 输入shell:startup 将快捷方式复制进来 就可以了 如果你有ccleaner&#xff0c;也可以看到

【爬虫】8.1. 使用OCR技术识别图形验证码

使用OCR技术识别图形验证码 文章目录 使用OCR技术识别图形验证码1. OCR技术2. 准备工作2.1. tesserocr安装异常 3. 验证码图片爬取4. 无障碍识别测试5. 错误识别6. 识别实战&#xff1a;7. 参数设置 图形验证码是最早出现的验证方式&#xff0c;现在依然很常见&#xff0c;一般…

【uni-app】

准备工作 1.下载hbuilder&#xff0c;插件使用Vue3的uni-app项目 2.需要安装编译器 3.下载微信开发者工具 4.点击运行->微信开发者工具 5.打开微信开发者工具的服务端口 效果图 page.json&#xff08;添加路由&#xff0c;修改底层导航栏&#xff0c;背景色&#xff09…

安达发|APS排程系统解决各类制造业难题方案

APS(Advanced Product Scheduling,先进产品计划)软件是一种基于计算机技术的生产计划和调度系统&#xff0c;广泛应用于汽车制造、电子制造、注塑、化工、纺织等行业。本文将详细介绍APS软件在这些行业的应用场景及其优势。 一、汽车制造 1. 零部件生产计划&#xff1a;APS软件…

QEM网格简化算法学习

《Surface Simplification Using Quadric Error Metrics》这篇论文介绍了一种网格简化的算法&#xff0c;通过“edge contraction”&#xff08;边收缩&#xff09;的方法来简化网格。边收缩的结果就是将两个顶点合成一个顶点&#xff0c;因此可以按照任意的顶点数目去简化网格…

2023国赛C题解题思路代码及图表:蔬菜类商品的自动定价与补货决策

2023国赛C题&#xff1a;蔬菜类商品的自动定价与补货决策 C题表面上看上去似乎很简单&#xff0c;实际上23题非常的难&#xff0c;编程难度非常的大&#xff0c;第二题它是一个典型的动态规划加仿真题目&#xff0c;我们首先要计算出销量与销售价格&#xff0c;批发价格之间的…

MySQL的概述、版本、安装过程

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 目录 一、MySQL的概述 二、MySQL的版本 三、MySQL的下载与安装 前言 本文将来谈谈MySQL的概述&#xff0c;MySQL的版本&#xff0c;以及它…

浙大MBA提面苏州/上海批周末申请截止:仅剩杭州第五批可选

9月10日一过&#xff0c;2024年浙大MBA提前批面试将正式迎来最后一批申请&#xff01;还没开始申请的伙伴要抓紧时间了&#xff0c;按照惯例&#xff0c;最后一批一般在时间节奏上都是最为紧张的&#xff01; 回顾今年的提前批面试申请历程&#xff0c;虽然在总体人数上…

企业内训课程、在线教育平台付费课程加密防下载的10种方式

企业内训课程、在线教育平台付费课程加密防下载的10种方式&#xff1a; 实例演示&#xff1a;课程视频-第1课状语从句,VRM演示应用 企业内训课程、在线教育平台付费课程&#xff0c;他们的这种视频课程的加密是如何做的&#xff1f;整理了10种思路&#xff0c;供大家参考&…

山西电力市场日前价格预测【2023-09-09】

日前价格预测 预测明日&#xff08;2023-09-09&#xff09;山西电力市场全天平均日前电价为372.85元/MWh。其中&#xff0c;最高日前电价为435.72元/MWh&#xff0c;预计出现在18: 45。最低日前电价为342.46元/MWh&#xff0c;预计出现在04: 00。 价差方向预测 1&#xff1a; 实…