Java硬编码开关(switch)与哈希表(hashmap)的比较

14

有一个消息类可以根据标签号返回标签名

由于该类被实例化多次,我有点不愿意为每个实例创建一个HashMap:

public class Message {
  private HashMap<Integer,String> tagMap;

  public Message() {
    this.tagMap = new HashMap<Integer,String>();
    this.tagMap.put( 1, "tag1Name");
    this.tagMap.put( 2, "tag2Name");
    this.tagMap.put( 3, "tag3Name");
  }

  public String getTagName( int tagNumber) {
    return this.tagMap.get( tagNumber);
  }
}

支持硬编码的理由:

public class Message {
  public Message() {
  }

  public String getTagName( int tagNumber) {
    switch( tagNumber) {
      case 1: return "tag1Name";
      case 2: return "tag2Name";
      case 3: return "tag3Name";
      default return null;
    }
  }
}

当你把所有东西都考虑进去(内存,性能,垃圾回收,...)

有没有理由坚持使用HashMap?


2
这不是适合使用“枚举(enum)”的情况吗? - Edd
5
如果你所有的消息都使用相同的列表,你也可以将该映射设为静态的。 - assylias
1
另外,您是否(通过分析)确定类的实例化确实是一个问题? - joergl
如果地图是静态的,那么对put()的后续调用会发生什么? - MonoThreaded
@AknownImous 没有什么特别的,新值只是被添加到地图中... - assylias
显示剩余2条评论
5个回答

6

在静态块中初始化MAP

由于您将创建许多Message对象,因此应编写如下代码:

public class Message {

  private static HashMap tagMap;

  static {
     tagMap = new HashMap();
     tagMap.put( 1, "tag1Name");
     tagMap.put( 2, "tag2Name");
     tagMap.put( 3, "tag3Name");
  }

  public Message() {

  }

  public String getTagName( int tagNumber) {
    return tagMap.get( tagNumber);
  }
}

抱歉...Map使我们能够编写整洁的代码。使用Map和Switch之间的时间差异不大。 - Byter
@JamesB:静态初始化块为什么不好,有什么替代方案吗? - joergl
@JamesB 我仍然不明白为什么使用静态块是一件坏事? - Byter
1
@joergl 我更倾向于不使用它们。通常这里有很多替代方案,正如众多回答所示。此外,在单元测试时,你通常需要编写额外的代码来处理它们。这只是我的个人意见。 - JamesB
1
你甚至可能想要添加 `tagMap = Collections.unmodifiableMap(tagMap);`当然,前提是在静态块之后运行时地图不会更改。 - Pasukaru
显示剩余7条评论

1

Map 可以作为命令模式使用,其中键表示条件,值表示要执行的命令。唯一的缺点是在使用之前会创建对象,因此如果您有大量这样的条件,则可以选择 Map,否则如果您的条件很少,则 switch 总是更加优雅的方法。


0

这取决于你的需求。例如,如果你需要获取所有标签名称以便显示,使用 Map 会很有帮助。此外,如果你使用 TreeMap 替换,你可以得到排序后的结果。
如果你没有这样的需求,那么使用 Map 就会增加额外负担,你可以选择使用 Enum 或者其他更高效的方法(虽然相比于 5-10-20 个 case 选项,可读性会稍差一些)。


0
为什么不将 getTagName 方法设为静态的,并从属性文件中进行延迟加载呢?
public static String getTagName(int tagNumber) {
    if tagsByID == null) {
        // load tags from properties
    }
    return tagsByID.get(tagNumber);
}

易于测试和配置,无需重新编译。


属性文件意味着额外的可交付成果。由于列表相当静态,我宁愿嵌入映射。 - MonoThreaded

0
如果您的所有标签值在区间[1..n]中连续,则可以使用数组或可能是ArrayList并直接访问这些值。
public class Message {
    private ArrayList<String> tags;

    public Message() {
        this.tags =  = new ArrayList<String>();
        this.tags.add("Unknown");
        this.tags.add("tag1Name");
        this.tags.add("tag2Name");
        this.tags.add("tag3Name");
    }

    public String getTagName(int tagNumber) {
        return this.tags.get(tagNumber);
    }
}

使用数组的替代方案。

public class Message {
    private static final String[] tags = {
        "N/A",
        "tag1Name",
        "tag2Name",
        "tag3Name",
        null,
        null,
        "tag6Name",
    };

    public Message() {
    }


    public String getTagName(int tagNumber) {
        if (tagNumber < 0 || tagNumber > tags.length) {
            throw new IllegalArgumentException();
        return tags[tagNumber];
    }
}

很遗憾,这不是一个连续的列表。 - MonoThreaded
@AknownImous 真遗憾。但我还是会让我的回答保留下来。 - maba
@AknownImous 你知道标签是如何编号的吗?也许你可以在数组中允许一些间隔,而不会占用太多内存。 - maba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接