【问题标题】:Java hardcoded switch vs hashmapJava硬编码开关与哈希图
【发布时间】:2012-08-13 18:19:48
【问题描述】:

某些消息类能够根据标签号返回标签名称

由于这个类被实例化了很多次,我有点不愿意为每个实例创建一个 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;
    }
  }
}

当你把所有东西都放在一起时(内存、性能、GC、...)

有什么理由坚持使用 HashMap?

【问题讨论】:

  • 这不是enum 理想的情况吗?
  • 如果所有消息的列表都相同,您也可以将地图设为静态。
  • 另外,您是否(通过分析)确定类的实例化确实是一个问题?
  • 枚举不适合,因为某些标签编号可能未知
  • 如果地图是静态的,那么对 put() 的后续调用会发生什么?

标签: java performance hashmap


【解决方案1】:

在静态块中初始化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);
  }
}

【讨论】:

  • 我不知道如何在静态块中调用 put()。甜
  • 使用地图不能比使用开关快,这没有任何意义。
  • @Byter 是否有任何证据表明 Map(以及 Map 的什么实现)比 switch 更快?你真的测试过吗?
  • @joergl 我不想使用它们。正如这里的众多回应所证明的那样,通常总是有替代方案。另外,您通常必须在单元测试时编写额外的代码来处理它们。只是我的意见。
  • 您甚至可能想添加tagMap = Collections.unmodifiableMap(tagMap); 当然,前提是在静态块之后地图不会在运行时更改。
【解决方案2】:

Map 可以用作命令模式,其中键表示条件,值表示要执行的命令,唯一的缺点是对象在使用之前创建,所以如果您有大量这样的条件,那么您可以选择 map else switch如果您的条件很少,这始终是一种优雅的方法。

【讨论】:

    【解决方案3】:

    取决于你需要什么。例如,如果您曾经需要使用Map 获取所有标签名称以进行显示,那将得到回报。此外,如果您替换为 TreeMap,您可以对它们进行排序。
    如果您没有这样的需求,那么使用Map 将是一种开销,而您的方法或Enum 会更有效率(尽管您的可读性低于5-10-20 @ 的选项) 987654325@选项)

    【讨论】:

      【解决方案4】:

      为什么不将 getTagName 方法设为静态并从属性文件中延迟加载它?

      public static String getTagName(int tagNumber) {
          if tagsByID == null) {
              // load tags from properties
          }
          return tagsByID.get(tagNumber);
      }
      

      无需重新编译即可轻松测试和配置。

      【讨论】:

      • 属性文件意味着额外的交付物。由于列表是相当静态的,我宁愿嵌入映射。
      【解决方案5】:

      如果您的所有标记值在区间 [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];
          }
      }
      

      【讨论】:

      • 不幸的是它不是一个连续的列表
      • @AknownImous 太糟糕了。但无论如何我都会让我的答案保持不变。
      • @AknownImous 你知道标签是如何编号的吗?也许您可以在数组中允许一些间隙而不会占用太多内存。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-26
      • 2013-02-08
      相关资源
      最近更新 更多