初识Java 10-3 集合

news2024/11/24 20:20:44

目录

 Collection和Iterator的对比

for-in和迭代器

总结图


本笔记参考自: 《On Java 中文版》


 Collection和Iterator的对比

        Collection是所有序列集合的共同根接口。因此,可以认为它是一个为表示其他接口之间的共性而出现的“附属接口”。

        java.util.AbstractCollection提供了一个Collection的默认实现,所以可以通过创建AbstractCollection的新子类来避免不必要的代码重复。这种接口存在的另一个理由是,通过面向接口的编程方式,我们的代码可以变得更加通用。一个实现了接口的方法也可以应用于任何的Collection类型。

    在Java中,实现Collection需要提供iterator()方法,这就将迭代器和集合捆绑起来了。

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.List;

public class InterfaceVsIterator {
    public static void display(Iterator<Pet> it) {
        while (it.hasNext()) {
            Pet p = it.next();
            System.out.print(p.id() + ": " + p + " ");
        }
        System.out.println();
    }

    public static void display(Collection<Pet> pets) {
        for (Pet p : pets)
            System.out.print(p.id() + ": " + p + " ");
        System.out.println();
    }

    public static void main(String[] args) {
        List<Pet> petList = new PetCreator().list(8);
        Set<Pet> petSet = new HashSet<>(petList);

        Map<String, Pet> petMap = new LinkedHashMap<>();
        String[] names = ("拉尔夫, 埃里克, 罗宾, 蕾西, " + "布里特妮, 山姆, 斑点, 路威").split(", ");
        for (int i = 0; i < names.length; i++)
            petMap.put(names[i], petList.get(i));

        // Collection
        display(petList);
        display(petSet);

        // Iterator
        System.out.println();
        display(petList.iterator());
        display(petSet.iterator());

        System.out.println();
        System.out.println(petMap);
        System.out.println(petMap.keySet());
        display(petMap.values());
        display(petMap.values().iterator());
    }
}

        程序执行的结果是:

(笔者使用的是JDK 11,输出结果与《On Java》中有所出入。推测是因为哈希的实现有区别。)

        在上述程序中可以发现,Collection和Iterator都实现了解耦,display()方法不需要理解底层集合的特定实现。

        若要实现一个不是Collection的外部类,让其实现Collection接口可能会很麻烦或是复杂的,这时就会体现出Iterator的优势了。下面的例子将会继承AbstractCollection类:

import java.util.AbstractCollection;
import java.util.Iterator;

public class CollectionSequence extends AbstractCollection {
    private Pet[] pets = new PetCreator().array(8); // 返回一个Pet[]数组

    @Override
    public int size() { // 必须实现的接口方法size()
        return pets.length;
    }

    @Override
    public Iterator<Pet> iterator() { // 必须自己提供iterator()方法
        return new Iterator<Pet>() { // Java的类型推断能力有限,所以这里还是需要标明类型
            private int index = 0;

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

            @Override
            public Pet next() {
                return pets[index++];
            }

            @Override
            public void remove() { // remove是可选的实现,就算这里不进行实现也没有关系
                throw new UnsupportedOperationException();
            }
        };
    }

    public static void main(String[] args) {
        CollectionSequence c = new CollectionSequence();
        InterfaceVsIterator.display(c);
        InterfaceVsIterator.display(c.iterator());
    }
}

        程序执行的结果如下:

        这个例子实现了一个Collection,为此还提供了一个iterator()的实现。但这里我们就发现,与继承AbstractCollection类相比,只实现iterator()所需的工作并没有减少太多。另外,实现Collection还需要提供我们并不需要使用的其他方法的实现。

        所以,先继承,在添加创建迭代器的能力,这样会比较轻松:

    生成一个Iterator,是将序列与处理序列的方法连接起来的耦合性最低的方法。与Collection相比,这种做法对序列类的约束会少的多。

for-in和迭代器

        for-in语法可以配合任何Collection对象使用:

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;

public class ForInCollections {
    public static void main(String[] args) {
        Collection<String> cs = new LinkedList<>();
        Collections.addAll(cs, "不吃葡萄倒吐葡萄皮".split(""));
        for (String s : cs)
            System.out.print("'" + s + "'");
        System.out.println();
    }
}

        程序执行的结果如下:

        for-in语句之所以能这么做,其原理是因为Java 5引入了一个叫做Iterable的接口,这个接口包含的iterator()方法会生成一个Iteratorfor-in使用这个Iterable接口遍历序列。

        举一反三,若我们创建了一个实现了Iterable接口的类,那么这个类也就可以被用于for-in语句中:

import java.util.Iterator;

public class IterableClass implements Iterable<String> {
    protected String[] words = ("扁担没有板凳宽,板凳没有扁担长".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() {
                throw new UnsupportedOperationException();
            }
        };
    }

    public static void main(String[] args) {
        for (String s : new IterableClass())
            System.out.print(s + " ");
        System.out.println();
    }
}

        程序执行的结果如下:

        for-in语句可以配合数组或实现了Iterable接口的类进行使用,但这并不是说数组也自动实现了Iterable,也并不存在任何装箱操作:

import java.util.Arrays;

public class ArrayIsNotIterable {
    static <T> void test(Iterable<T> ib) {
        for (T t : ib)
            System.out.print(t + " ");
        System.out.println();
    }

    public static void main(String[] args) {
        test(Arrays.asList(1, 2, 3));

        String[] strings = { "A", "B", "C" };
        // 数组可以配合for-in进行使用
        // 但并没有实现Iterable接口,因此无法作为参数传入test()
        // test(strings);

        // 必须将strings显式地转换为Iterable:
        test(Arrays.asList(strings));
    }
}

        程序执行的结果如下:

适配器方法惯用法

        当我们需要以不止一种方式将类用在for-in语句时,继承就不能很好地满足我们了。此时,我们需要做的是提供一个满足for-in语句要求的特定接口。比如:默认的迭代器是向前的,若我们还需要进行向后的迭代,那么一个好的方法就是添加一个生成Iterable对象的方法:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

class ReversibleArrayList<T> extends ArrayList<T> {
    ReversibleArrayList(Collection<T> c) {
        super(c);
    }

    public Iterable<T> reversed() { // 添加一个迭代器
        return new Iterable<T>() {
            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() { // 未实现
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }
}

public class AdapterMethodIdiom {
    public static void main(String[] args) {
        ReversibleArrayList<String> ral = new ReversibleArrayList<>(
                Arrays.asList("To be continued".split(" ")));

        // 通过iterator()获得原始的迭代器
        for (String s : ral)
            System.out.print(s + " ");
        System.out.println();

        // 使用自建的迭代器
        for (String s : ral.reversed())
            System.out.print(s + " ");
        System.out.println();
    }
}

        程序执行的结果是:

        在main(),可以看见调用ralral.reversed()产生的是不同的行为。

        另外,迭代器也可以通过使用其他类来实现:

        上述这个类将会返回一个被打乱的Iterator

        最后再提一下Arrays.asList(),这个方法会返回一个包装过的ArrayList。若将包装后的ArrayList传递给Collections.shuffle(),那么原始数组不会受到影响。但若直接将Arrays.asList()生成的List打乱顺序,那么shuffle()方法会改变底层数组:

import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;

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 };

        List<Integer> list1 = new ArrayList<>(Arrays.asList(ia)); // 
        System.out.println("在乱序之前:" + list1);
        Collections.shuffle(list1, rand);
        System.out.println("在乱序之后:" + list1);
        System.out.println("数组本身:" + Arrays.toString(ia));

        System.out.println();
        List<Integer> list2 = Arrays.asList(ia);
        System.out.println("在乱序之前:" + list2);
        Collections.shuffle(list2, rand);
        System.out.println("在乱序之后:" + list2);
        System.out.println("数组本身:" + Arrays.toString(ia));
    }
}

        程序执行的结果如下:

        注意:Arrays.asList()产生的List对象,会将原本的底层数组作为其物理实现。所以,若需要修改List,最好将其复制到另一个集合中。

总结图

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

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

相关文章

Linux下ThinkPHP5实现定时器任务 - 结合crontab

实例一&#xff1a; 1.在/application/command创建要配置的PHP类文件&#xff0c;需要继承Command类&#xff0c;并重写configure和execute两个方法&#xff0c;例如: <?php namespace app\command; use think\console\Command; use think\console\Input; use think\cons…

紫光展锐6nm国产5G处理器T820_国产手机芯片5G方案

紫光展锐T820是一款采用先进6nm EUV工艺的芯片&#xff0c;采用134三丛集八核心CPU架构&#xff0c;由1个主频为 2.7GHz 的 Arm Cortex-A76 大核和 3个主频为2.3GHz 的Arm Cortex-A76大核以及4个主频为2.1GHz的 Arm Cortex-A55组成 &#xff0c;支持高达3MB 三级缓存&#xff0…

【解决方案】edge浏览器批量添加到集锦功能消失的解决方案

edge的集锦功能很好用&#xff0c;右键标签页会出现如下选项&#xff1a; 但在某次edge更新后&#xff0c;右键标签页不再出现该选项&#xff1a; 这里可以参考为什么我的Edge浏览器右键标签页没有“将所有标签页添加到集锦”功能&#xff1f; - Microsoft Community 一文提出…

1794_ChibiOS网络书籍的介绍阅读

全部学习汇总&#xff1a; GreyZhang/g_ChibiOS: I found a new RTOS called ChibiOS and it seems interesting! (github.com) 看到这个介绍我觉得这个OS的作者也是一个超级hack&#xff0c;而且非常有工匠精神。为什么要推出一个全新的RTOS呢&#xff0c;其实主要的原因就是觉…

【C语言】插入排序详解

文章目录 一、直接插入排序1、插入排序思想2、程序代码3、测试 二、希尔排序1、什么是希尔排序2、希尔排序图解3、程序代码4、测试 一、直接插入排序 1、插入排序思想 直接插入排序就是将待排序的记录按照它的关键码值插入到一个已经排好序的有序序列中&#xff0c;直到所有的…

【亲测有效】解决npm报错:RequestError: unable to verify the first certificate

问题简述 帖主从nodejs官网下载安装nodejs后&#xff0c;发现使用以下命令安装electron会报错 npm install electron报错信息如下&#xff1a; npm ERR! RequestError: unable to verify the first certificate解决方案 网上列举的方案&#xff0c;无外乎&#xff1a; 设置…

一篇文章让你学会什么是哈希

一篇文章让你学会什么是哈希 哈希概念哈希冲突哈希函数1. 直接定址法2. 除留余数法3. 平方取中法4. 折叠法5. 随机数法6. 数学分析法 哈希冲突解决1. 闭散列1.1 线性探测1.2 二次探测 2. 开散列 开散列和闭散列对比 哈希概念 哈希在C中有广泛的应用&#xff0c;它是一种用于快…

Compose LazyColumn 对比 RecyclerView ,谁的性能更好?

LazyColumn 是 compose 中用来实现类似 RecyclerView 效果的控件 &#xff0c;但是大家都说LazyColumn性能比RecyclerView差太多&#xff0c;毕竟 RecyclerView google优化了十多年了&#xff0c;比RecyclerView差一点也正常&#xff0c;今天我们就用实际数据来对比LazyColumn和…

全流程HEC-RAS 1D/2D水动力与水环境模拟技术案例实践及拓展应用丨从小白到精通,十九项案例实践

目录 专题一 水动力模型基础 专题二 恒定流模型(1D/2D) 专题三 一维非恒定流 专题四 二维非恒定流模型&#xff08;一&#xff09; 专题五 二维非恒定流模型&#xff08;二&#xff09; 专题六 HEC-RAS的水质模型 专题七 高级主题 水动力与水环境模型的数值模拟是实现水…

如何安全传输存储用户密码?(程序员必备)

前言 我们开发网站或者APP的时候&#xff0c;首先要解决的问题&#xff0c;就是「如何安全传输和存储用户的密码」。一些大公司的用户数据库泄露事件也时有发生&#xff0c;带来非常大的负面影响。因此&#xff0c;如何安全传输存储用户密码&#xff0c;是每位程序员必备的基础…

基于springboot+vue的大学生科创项目在线管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

Git大全

目录 一、Git概述 1.1Git简介 1.2Git工作流程图 1.3查看Git的版本 1.4 Git 使用前配置 1.5为常用指令配置别名&#xff08;可选&#xff09; 1.5.1打开用户目录&#xff0c;创建 .bashrc 文件 1.5.2在 .bashrc 文件中输入如下内容&#xff1a; 1.5.3打开gitBash&#xff0c;执行…

若依框架集成WebSocket带用户信息认证

一、WebSocket 基础知识 我们平时前后台请求用的最多的就是 HTTP/1.1协议&#xff0c;它有一个缺陷&#xff0c; 通信只能由客户端发起&#xff0c;如果想要不断获取服务器信息就要不断轮询发出请求&#xff0c;那么如果我们需要服务器状态变化的时候能够主动通知客户端就需要用…

商家收款码费率是什么意思

今天&#xff0c;我将分享如何有效地降低日常中的收款手续费率。我们都知道&#xff0c;不管是微信支付还是支付宝&#xff0c;平台都会从中扣除一定的手续费。但你是否知道&#xff0c;其实手续费率是可以降低的呢&#xff1f;今天介绍如何申请最低手续费率为0.2%的方法&#…

【从0学习Solidity】26. 删除合约

【从0学习Solidity】26. 删除合约 博主简介&#xff1a;不写代码没饭吃&#xff0c;一名全栈领域的创作者&#xff0c;专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构&#xff0c;分享一些项目实战经验以及前沿技术的见解。关注我们的主页&#xff0c;探索全栈…

酒店外卖小程序商城的作用是什么

随着线上餐品销售属性增强&#xff0c;传统酒店除了承接到店客户&#xff0c;外送也成为生意的一部分&#xff0c;但传统打电话、微信发送的方式无法实现餐品全面呈现和客户随时订购需求&#xff0c;在配送方面也无法规范化。 除此之外&#xff0c;还需要完善营销、会员管理、…

好玩的调度技术

好玩的调度技术 文章目录 好玩的调度技术前言一、乱金柝-空间剥离二、拖拽编辑三、全端兼容 前言 最近感觉自己抑郁了&#xff0c;生态技术实在太庞大太复杂&#xff0c;所以我决定先停一段时间&#xff0c;在停下写生态的这两天写了几个调度的小玩意换换脑子&#xff0c;很有…

java Spring MVC主动解决请求跨域问题,别再依靠前端代理帮你解决问题啦,老铁!!

前端发展至今 已经有了很多代理工具可以帮助我们解决跨域 但说到底 跨域还是后端能解决的话最好 那么 我们先来看一个请求 很明显 这个接口发生了跨域 因为这里我是用的Spring MVC 而 Spring MVC 解决跨域的代码非常简单 我们只需要加一个注解 CrossOrigin 参考代码如下 Cr…

冒烟测试到底多重要?

冒烟测试介绍 冒烟测试一词&#xff0c;来源于电路板测试&#xff1a;电路板拼接或组装完成后&#xff0c;进行通电测试&#xff0c;如果冒烟&#xff0c;则说明存在缺陷。 而软件应用中&#xff0c;对其的定义为&#xff1a;在软件开发过程中的一种针对软件版本包的快速基本…

LabVIEW崩溃问题解决方法

LabVIEW崩溃问题解决方法 LabVIEW在运行中出现崩溃的情况&#xff0c;确实让人很崩溃。不过按照下面的方法可以逐步排查解决。 在LabVIEW开发环境中浏览时&#xff0c;LabVIEW崩溃并显示以下错误&#xff1a; 解决方案 LabVIEW内部错误和崩溃的初步故障排除步骤&#xff1a;…