zoukankan      html  css  js  c++  java
  • 第33条:用EnumMap代替序数索引

     有时候,会见到利用ordinal方法来索引数组的代码。例如下面这个简化的类,表示一种烹饪用的香草:

    public class Herb {
        public enum Type { ANNUAL, PERENNIAL, BIENNIAL }
        
        private final String name;
        private final Type type;
        
        Herb(String name, Type type) {
            this.name = name;
            this.type = type;
        }
        
        @Override
        public String toString() {
            return name;
        }
        
    }

     假设有一个香草的数组,表示一座花园中的植物,想要按照类型(一年生、多年生或者两年生植物)进行组织后将植物列出来。

    1.将集合放到一个按照类型的序数进行索引的数组中实现:

    public static void main(String[] args) {
            Herb[] garden = {new Herb("a", Herb.Type.ANNUAL), new Herb("b", Herb.Type.BIENNIAL)};
    
            Set<Herb>[] herbsByType = (Set<Herb>[]) new Set[Herb.Type.values().length];
            for(int i=0; i < herbsByType.length; i++)
                herbsByType[i] = new HashSet<Herb>();
            for(Herb h : garden)
                herbsByType[h.type.ordinal()].add(h);
            for(int i=0; i < herbsByType.length; i++)
                System.out.printf("%s: %s%n", Herb.Type.values()[i], herbsByType[i]);
    }

    这种方法可行,但是由于数组与泛型不兼容,需要进行未受检的转换。

    2.更好的方法,数组实际上充当从枚举到值的映射,EnumMap提供这样的实现:

    public static void main(String[] args) {
            Herb[] garden = {new Herb("a", Herb.Type.ANNUAL), new Herb("b", Herb.Type.BIENNIAL)};
            Map<Herb.Type, Set<Herb>> herbByType = new EnumMap<Herb.Type, Set<Herb>>(Herb.Type.class);
            for(Herb.Type t : Herb.Type.values())
                herbByType.put(t, new HashSet<Herb>());
            for(Herb h : garden)
                herbByType.get(h.type).add(h);
            System.out.println(herbByType);
    }

    不存在不安全的类型转换。

    还可能遇到按照序数进行两次索引的数组的数组

    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()];
            }
        }
    }

    利用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);
            }
        }
    }

     不需要平方级大小的数组。

    如果增加一个新的阶段plasma(离子),只有两个过渡与这个阶段关联,电离化,将气体变成离子,消电离化,将离子变成气体。

    基于数组的程序,必须给Phase添加一个PLASMA常量,给Phase.Transition添加两种常量,用新的16个元素的版本取代原来9个元素的数组的数组,这很容易出错。

    基于EnumMap的版本,所要做的只是将PLASMA添加到Phase列表,并把IONIZE(GAS, PLASMA), DEIONIZE(PLASMA, GAS) 添加到Phase.Transition列表中,出错的概率很小。

  • 相关阅读:
    链表 | 递归删除不带头结点链表所有x元素
    A1016 | 磨人的大模拟
    Ubuntu 下安装 Qt Designer
    shell札记
    A1102 | 反转二叉树
    顺序表 | 二分查找:两个数组合并后的中位数
    A1113 | Integer Set Partition (25)
    A1128 | 逻辑想象能力、简洁高效美观的代码、memset的使用情景
    05.字符串
    04.序列的应用
  • 原文地址:https://www.cnblogs.com/13jhzeng/p/5730151.html
Copyright © 2011-2022 走看看