【问题标题】:How to convert Map<String, String> to Map<Long, String> ? (option : using guava)如何将 Map<String, String> 转换为 Map<Long, String> ? (选项:使用番石榴)
【发布时间】:2011-04-20 15:52:21
【问题描述】:

我有一个Map&lt;String, String&gt; String 键只是数值,例如“123”等。我得到数值是因为这些值来自我的 JSF 组件中的 UI。我不想更改 UI 组件的合同。

现在我想基于上面的Map 创建一个Map&lt;Long, String&gt;,我在Maps 类中看到了一些transform 方法,但都专注于转换值而不是键。

有没有更好的方法将Map&lt;String, String&gt; 转换为Map&lt;Long, String&gt;

【问题讨论】:

标签: java guava


【解决方案1】:

@Vivin 的回答是正确的,但我认为解释为什么 Guava 没有任何方法可以让您转换 Map 的键(或根本不转换 Set)是有用的。

Guava 的所有转换和过滤方法都会产生惰性结果...函数/谓词仅在使用对象时需要时应用。他们不创建副本。不过,正因为如此,转换很容易打破Set 的要求。

例如,假设您有一个Map&lt;String, String&gt;,其中包含“1”和“01”作为键。它们都是不同的Strings,因此Map 可以合法地包含两者作为键。但是,如果您使用Long.valueOf(String) 转换它们,它们都映射到值1。它们不再是不同的键。如果您创建地图副本并添加条目,这不会破坏任何内容,因为任何重复的键都会覆盖该键的先前条目。但是,延迟转换的 Map 将无法强制执行唯一键,因此会破坏 Map 的合同。

【讨论】:

  • 对不起,我后来才看到番石榴标签:p
【解决方案2】:

Java 8 更新

您可以使用流来执行此操作:

Map<Long, String> newMap = oldMap.entrySet().stream()
  .collect(Collectors.toMap(e -> Long.parseLong(e.getKey()), Map.Entry::getValue));

这假定所有键都是Longs 的有效字符串表示形式。另外,变形时可能会发生碰撞;例如,"0""00" 都映射到 0L


我认为您必须遍历地图:

Map<Long, String> newMap = new HashMap<Long, String>();
for(Map.Entry<String, String> entry : map.entrySet()) {
   newMap.put(Long.parseLong(entry.getKey()), entry.getValue());
}

此代码假定您已清理 map 中的所有值(因此没有无效的长值)。

我希望有更好的解决方案。

编辑

我在 Commons Collection-Utils 中遇到了 CollectionUtils#transformedCollection(Collection, Transformer) 方法,看起来它可以满足您的需求。 暂且不提,它仅适用于实现 Collection 的类。

【讨论】:

  • @Falcon :) 即使有转换方法,内部实现也必须类似。即O(n)。我想不出任何比O(n)更好的方法。
  • 这与复杂性无关,但如果已经编写了某些内容,或者可以使用 guava 的函数方法轻松实现(我已经编写了函数)并且当然具有相当的速度/效率,那么我不需要这样做。
  • @Falcon,这将是我的下一条评论 :) 我希望有一种 功能性 或更优雅的方式来做到这一点 :)
【解决方案3】:

您现在可以使用 Java 8 流、映射、收集以更易读、更干净的方式执行此操作。

Map<String, String> oldMap

Map<Long, String> newMap = oldMap.entrySet().stream()
  .collect(Collectors.toMap(entry -> Long.parseLong(entry.getKey()), Map.Entry::getValue));

【讨论】:

  • 完美答案!
【解决方案4】:

这是以下答案之一的更新版本,以使生成的 Map 不可修改(不,它不是使用 Guava,只是使用普通的 Java 8):

  import static java.util.stream.Collectors.collectingAndThen;
  import static java.util.stream.Collectors.toMap;

  ...

  newMap = oldMap.entrySet().stream().collect(collectingAndThen(
              toMap((Map.Entry<String, String> entry) -> transformKey(entry.getKey()),
                    (Map.Entry<String, String> entry) -> transformValue(entry.getValue())),
              Collections::unmodifiableMap)));

【讨论】:

    【解决方案5】:

    简短的回答是否定的,Guava 不提供开箱即用的这个。

    简单的方法如下所示。不过也有一些注意事项。

        public static <K, V, L, W> Map<L, W> transformMap(Map<K, V> map, Function<K, L> keyFunction, Function<V, W> valueFunction) {
        Map<L, W> transformedMap = newHashMap();
    
        for (Entry<K, V> entry : map.entrySet()) {
            transformedMap.put(
                    keyFunction.apply(entry.getKey()),
                    valueFunction.apply(entry.getValue()));
        }
    
        return transformedMap;
    }
    
    public static <K, V, L> Map<L, V> transformKeys(Map<K, V> map, Function<K, L> keyFunction) {
        return transformMap(map, keyFunction, Functions.<V>identity());
    }
    

    Guava 的转换器都是“惰性的”或基于视图的。我认为要实现映射键转换器,您需要一个双向功能。我的理解是 Guava 团队正在开发一个转换器,可以解决这个问题。

    您遇到的另一个问题是您必须处理重复的可能性才能成为“Jimmy-proof”,这是另一个番石榴原则。处理该问题的一种方法是返回 Multimap;另一种方法是在遇到重复项时抛出异常。我不建议隐藏问题,例如通过忽略具有重复键的后续条目,或使用重复键覆盖新条目。

    【讨论】:

      【解决方案6】:

      使用 Java 8 和jOOλ回答

      private static <K1, K2, V> Map<K2, V> mapKeys(Map<K1, V> map, Function<K1, K2> keyMapper) {
          return Seq.seq(map).map(t -> t.map1(keyMapper)).toMap(Tuple2::v1, Tuple2::v2);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-24
        • 2014-01-29
        • 1970-01-01
        • 1970-01-01
        • 2016-07-29
        相关资源
        最近更新 更多