第三十七条:不要以序号作为索引,使用EnumMap代替

news2024/9/22 22:35:16

我们有时可能会看到使用ordinal方法来索引数组或者列表的代码。例如,考虑到下面这个简单的类,用于表示一种植物:

public class Plant {
    enum LifeCycle {ANNUAL, PERENNIAL, BIENNIAL}

    final String name;
    final LifeCycle lifeCycle;

    public Plant(String name, LifeCycle lifeCycle) {
        this.name = name;
        this.lifeCycle = lifeCycle;
    }

    @Override
    public String toString() {
        return name;
    }
}

现在假设有一个表示花园的植物数组,我们想按照生长周期(一年、多年生或者两年生)列出这些植物。要做到这点,我们要构建三个集合,每个生长周期一个,然后遍历整个花园,将每个植物放入相应的集合中。有些程序员会这么做:把这些集合放在一个数组中,以生长周期的序号来索引。

public static void main(String[] args) {
        List<Plant> garden = new ArrayList<>();
        garden.add(new Plant("oneYear", Plant.LifeCycle.ANNUAL));
        garden.add(new Plant("twoYears", Plant.LifeCycle.PERENNIAL));
        garden.add(new Plant("twoYears", Plant.LifeCycle.PERENNIAL));
        garden.add(new Plant("manyYears", Plant.LifeCycle.BIENNIAL));
        garden.add(new Plant("manyYears", Plant.LifeCycle.BIENNIAL));
        garden.add(new Plant("oneYear", Plant.LifeCycle.ANNUAL));

        // 不推荐使用ordinal()
        Set<Plant>[] plantsOrder = new Set[Plant.LifeCycle.values().length];
        for (int i = 0; i < plantsOrder.length; i++) {
            plantsOrder[i] = new HashSet<>();
        }
        for (Plant p : garden) {
            plantsOrder[p.lifeCycle.ordinal()].add(p);
        }
        for (int i = 0; i < plantsOrder.length; i++) {
            System.out.printf("%s:%s\n", Plant.LifeCycle.values()[i], plantsOrder[i]);
        }


        System.out.println(plantsOrder);

    }

打印:

ANNUAL:[oneYear, oneYear]
PERENNIAL:[twoYears, twoYears]
BIENNIAL:[manyYears, manyYears]
[Ljava.util.Set;@23ab930d

Process finished with exit code 0

如上虽然可行,但有许多问题

  • 数组不能与泛型共用,存在Unchecked异常
  • 数组不知道索引代表的意义(如上0存储的为ANNUAL枚举),需要人为去处理

而使用EnumMap可避免上述问题,以Enum为键进行存储,而不是以它的ordinal(),如果是 多维的,可以使用 EnumMap<…,EnumMap<…>>

Map<Plant.LifeCycle, Set<Plant>> plantsOrder = new EnumMap<>(Plant.LifeCycle.class);
        for (Plant.LifeCycle type : Plant.LifeCycle.values()) {
            plantsOrder.put(type, new HashSet<>());
        }
        for (Plant p : garden) {
            plantsOrder.get(p.lifeCycle).add(p);
        }
        System.out.println(plantsOrder);

打印:

{ANNUAL=[oneYear, oneYear], PERENNIAL=[twoYears, twoYears], BIENNIAL=[manyYears, manyYears]}

Process finished with exit code 0

注意:enumMap构造器接收的是其键的类型对应的class对象:这是一个有限制的类型令牌,它提供了运行时的泛型信息。

有时候我们还会看到使用序号来索引的数组的数组(序号用了两次),用来表示来自两个枚举值的映射。例如下面的程序,它使用了这样的一个数组来将两个相映射到一个相变(从液态到固态是凝固,从液态到气态是沸腾的,等等):

// 不推荐这么做
public enum Phase {
    SOLID, LIQUID, GAS;

    public enum Transition {
        MELT, FREEZE, BOTL, CONDENSE, SUBLIME, DEPOSIT;
        private static final Transition[][] TRANSITIONS = {
                {null, MELT, SUBLIME},
                {FREEZE, null, BOTL},
                {DEPOSIT, CONDENSE, null}
        };

        public static Transition from(Phase src, Phase dst) {
            return TRANSITIONS[src.ordinal()][dst.ordinal()];
        }
    }

    public static void main(String[] args) {
        System.out.println(Transition.from(GAS,LIQUID ));
    }
}

打印:

CONDENSE

Process finished with exit code 0

这段代码的意思是说,要进行二维数组组合,通过方法去取对应的值,看起来没问题,但实际上如果和第一个案例差不多,并且如果添加想新的植物,扩展不太方便,同理,可以使用EnumMap来修改:

public enum Phase {
    SOLID, LIQUID, GAS;

    public enum Transition {
        MELT(SOLID,LIQUID), FREEZE(LIQUID, SOLID),
        BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),
        SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID);

        private final Phase src;
        private final Phase dst;

        Transition(Phase src, Phase dst) {
            this.src = src;
            this.dst = dst;
        }

        private static final Map<Phase, Map<Phase, Transition>> m =
                new EnumMap<Phase, Map<Phase, Transition>>(Phase.class);
        static {
            for(Phase p : Phase.values())
                m.put(p, new EnumMap<Phase, Transition>(Phase.class));
            for(Transition t : Transition.values())
                m.get(t.src).put(t.dst, t);
        }

        public static Transition from(Phase src, Phase dst) {
            return m.get(src).get(dst);
        }
    }

    public static void main(String[] args) {
        System.out.println(Transition.from(GAS, SOLID));
    }

}

打印:

DEPOSIT

Process finished with exit code 0

我们也可以使用嵌套的enumMap实现添加一个新的相

 SOLID, LIQUID, GAS, PLASMA;

    public enum Transition {
        MELT(SOLID,LIQUID), FREEZE(LIQUID, SOLID),
        BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),
        SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID),
        IONIZE(GAS,PLASMA),DEIONZE(PLASMA,GAS);
        ...//其他代码不变

总而言之,序号不太适合用来索引数组,应该使用enumMap代替。程序员应该极少或绝对不使用ordinal方法。

所有文章无条件开放,顺手点个赞不为过吧!

                                                            

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

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

相关文章

JS执行机制(同步和异步)

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。 异步:在做这件事的同时&#xff0c;你还可以去处理其他事 他们的本质区别&#xff1a;这条流水线上各个流程的执行顺序不同。 同步任务 同步任务都在主线程上执行&#xff0c;形成一个执行栈。 异步…

7、论等保的必要性

数据来源&#xff1a;7.论等保的必要性_哔哩哔哩_bilibili 等级保护必要性 降低信息安全风险 等级保护旨在降低信息安全风险&#xff0c;提高信息系统的安全防护能力。 风险发现与整改 开展等级保护的最重要原因是通过测评工作&#xff0c;发现单位系统内外部的安全风险和脆弱…

【计网】从零开始掌握序列化 --- JSON实现协议 + 设计 传输\会话\应用 三层结构

唯有梦想才配让你不安&#xff0c; 唯有行动才能解除你的不安。 --- 卢思浩 --- 从零开始掌握序列化 1 知识回顾2 序列化与编写协议2.1 使用Json进行序列化2.2 编写协议 3 封装IOService4 应用层 --- 网络计算器5 总结 1 知识回顾 上一篇文章我们讲解了协议的本质是双方能够…

【JavaEE】多线程编程引入——认识Thread类

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯&#xff0c;你们的点赞收藏是我前进最大的动力&#xff01;&#xff01;希望本文内容能帮到你&#xff01; 目录 引入&#xff1a; 一&#xff1a;Thread类 1&#xff1a;Thread类可以直接调用 2&#xff1a;run方法 &a…

SpringBoot+thymeleaf竞赛报名系统

一、介绍 > 这是一个基于Spring Boot的后台管理系统。 > 使用了Mybatis Plus作为持久层框架&#xff0c;EasyExcel用于Excel操作&#xff0c;Thymeleaf作为前端模板引擎。 > 界面简洁&#xff0c;功能丰富&#xff0c;完成度比较高&#xff0c;适用于JAVA初学者作…

安国U盘量产工具系列下载地址

来源地址&#xff08;访问需要科学工具&#xff09;&#xff1a;AlcorMP (Последняя версия ALCOR U2 MP v23.08.07.00.H) – [USBDev.ru] 版本列表&#xff1a; AlcorMP&#xff08;最新版本的 ALCOR U2 MP v23.08.07.00.H&#xff09; AlcorMP是在Alcor Mic…

SpringBoot项目License证书生成与验证(TrueLicense) 【记录】

SpringBoot项目License证书生成与验证(TrueLicense) 【记录】 在非开源产品、商业软件、收费软件等系统的使用上&#xff0c;需要考虑系统的使用版权问题&#xff0c;不能随便一个人拿去在任何环境都能用。应用部署一般分为两种情况&#xff1a; 应用部署在开发者自己的云服务…

数据集-目标检测系列-火车检测数据集 train >> DataBall

数据集-目标检测系列-火车检测数据集 train >> DataBall 数据集-目标检测系列-火车检测数据集 数据量&#xff1a;1W 想要进一步了解&#xff0c;请联系 DataBall。 DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;不断增加…

跟李沐学AI:注意力机制、注意力分数

目录 不随意线索 随意线索 注意力机制 非参注意力池化层 参数化的注意力机制 注意力机制总结 注意力分数 拓展到高维度 加性模型&#xff08;Additive Attention&#xff09; 点积注意力机制&#xff08;Dot Product Attention&#xff09; 注意力分数总结 不随意线…

vscode 顶部 Command Center,minimap

目录 vscode 顶部 Command Center 设置显示步骤: minimap设置 方法一:使用设置界面 方法二:使用命令面板 方法三:编辑 settings.json 文件 左侧目录树和编辑器字体不一致: vscode 顶部 Command Center Visual Studio Code (VSCode) 中的 Command Center 是一个集中…

240912-设置WSL中的Ollama可在局域网访问

A. 最终效果 B. 设置Ollama&#xff08;前提&#xff09; sudo vim /etc/systemd/system/ollama.service[Unit] DescriptionOllama Service Afternetwork-online.target[Service] ExecStart/usr/bin/ollama serve Userollama Groupollama Restartalways RestartSec3 Environme…

Python redis 安装和使用介绍

python redis安装和使用 一、Redis 安装1.1、Windows安装 二、安装 redis 模块二、使用redis 实例1.1、简单使用1.2、连接池1.3、redis 基本命令 String1.3.1、ex - 过期时间&#xff08;秒&#xff09;1.3.2、nx - 如果设置为True&#xff0c;则只有name不存在时&#xff0c;当…

fiddler抓包08_抓Android手机请求

课程大纲 手机抓包&#xff0c;电脑端的设置和IOS端相同&#xff0c;设置一次即可&#xff0c;无需重复设置。 前提&#xff1a;电脑和手机连接同一个局域网 土小帽电脑和手机都连了自己的无线网“tuxiaomao”。 Step1. 电脑端设置 ① 打开Fiddler - 开启抓包&#xff08;F12…

django项目——图片上传到阿里云OSS对象存储

文章目录 实现图片上传到阿里云OSS对象存储1. 创建阿里云OSS对象存储2. 查询获取接口访问key和秘钥3. 安装阿里云的SDK集成到项目中使用3.1 python直接操作oss23.2 django配置自定义文件存储上传文件到oss 实现图片上传到阿里云OSS对象存储 1. 创建阿里云OSS对象存储 开发文档…

重磅!人工智能等级考试来了,考试免费,上海落户可以加分

目录 简要介绍 一、关心的问题 1. 什么是上海市高等学校信息技术水平考试&#xff1f; 2. 考试分几个级别&#xff1f;有哪些科目&#xff1f; 3. 哪些人可以进行报名&#xff1f; 4. 每名学生可以报考几个科目&#xff1f; 5. 有没有考试大纲&#xff1f; 6. 考试是否有…

[笔记]23年度展会信息— 吊钩 起升机构

1.吊钩的规格参数 5吨吊钩重26公斤 10吨64公斤。 另外一套型号&#xff0c;更轻&#xff1a; 不确定是结构设计还是用钢材质达到了减重效果。 看看重载双滑轮吊钩&#xff1a; 50吨&#xff0c;400公斤&#xff0c;只是吊钩。 然后是行车吊钩与钢丝绳的直径。这在计算空载吊…

Web Components之继承

我们在使用Web Components自定义组件的时候&#xff0c;我们需要继承HTMLElement这个浏览器内置对象&#xff0c;但是如果我要一些高级封装&#xff0c;给组件内置一些方法的话。我们就需要使用继承的方式&#xff0c;在父类中实现基本功能的封装。 1 父类的封装 以下是我的继…

一文搞懂UEFI

Hi&#xff01;早哦。今天又是宠读者的一天&#xff0c;应允聊聊UEFI。 文章目录 前言UEFI是什么&#xff1f;传统BIOSBIOS作为标准BIOS作为实现BIOS的工作原理传统BIOS的局限性传统BIOS启动过程 BIOS VS UEFIUEFI&#xff1f;UEFI概念EFI 系统分区EFI 变量EFI 的启动过程EFI 变…

【高分系列卫星简介——高分三号卫星(GF-3)】

高分三号卫星&#xff08;GF-3&#xff09; 高分三号&#xff08;GF-3&#xff09;是我国首颗高分辨率、C频段、多极化合成孔径雷达&#xff08;SAR&#xff09;卫星&#xff0c;由中国空间技术研究院北京空间飞行器总部设计部研制&#xff0c;并于2016年8月10日成功发射。该卫…

thop计算模型复杂度(params,flops)

thop安装 -pip install thop在线安装失败 -离线安装 github网址&#xff1a; pytorch-OpCounter:Count the MACs / FLOPs of your PyTorch model. - GitCode python setup.py install 测试&#xff1a; from options import config as c import os os.environ["CUD…