【问题标题】:Zip two lists into an immutable multimap in Java 8 with Guava?在 Java 8 中使用 Guava 将两个列表压缩成一个不可变的多图?
【发布时间】:2018-08-22 01:00:13
【问题描述】:

for 循环看起来像

ImmutableListMultiMap.<Key, Value>Builder builder 
    = ImmutableListMultiMap.<Key, Value>newBuilder();
for (int i = 0; i < Math.min(keys.length(), values.length()); i++) {
  builder.put(keys.at(i), values.at(i));
}

Guava / Java 8 可能的第一步是

Streams.zip(keys, values, zippingFunction)

我认为zippingFunction 需要返回一个地图条目,但没有可公开构造的列表条目。所以我可以写这个的“最”功能的方式是使用返回一个 Pair 的压缩函数,我不确定它是否存在于 Guava 中,或者返回一个二元素列表,这是一个不正确的可变类型。正好是 2 个元素。

如果我可以创建地图条目,这将是理想的:

Streams.zip(keys, values, zippingFunction)
.collect(toImmutableListMultimap(e -> e.getKey(), e.getValue())

这似乎是最好的方法,但它是不可能的,并且压缩到条目和从条目中解压缩仍然似乎是迂回的。有没有办法让这成为可能或可以改进?

【问题讨论】:

  • 在尝试组合地图和流时,通常需要这种迂回。但是Maps::immutableEntry 在这里的配对功能还不错。

标签: java java-8 functional-programming guava multimap


【解决方案1】:

我认为您的程序代码已经是最佳解决方案(在内存和速度方面,假设是随机访问列表)。稍作修正,以便您的代码编译,它将是:

ImmutableListMultimap.Builder<Key, Value> builder = ImmutableListMultimap.builder();
for (int i = 0; i < Math.min(keys.size(), values.size()); i++) {
  builder.put(keys.get(i), values.get(i));
}
return builder.build();

如果您真的想使用流来“发挥作用”,那么压缩两个流是可行的方法,但您仍然必须在收集到多图之前创建中间“对”对象。您声称“没有可公开构建的列表条目”,但事实并非如此,您可以在此处使用 JDK 的 SimpleImmutableEntry 和 Guava 的 Maps.immutableEntry(它们比更通用的 Pair 更合适,其中,在事实上,在 JDK 或 Guava 中都找不到。

使用Streams#zip 需要传递流,因此最终代码如下所示:

Streams.zip(keys.stream(), values.stream(), SimpleImmutableEntry::new)
    .collect(toImmutableListMultimap(Map.Entry::getKey, Map.Entry::getValue));

如果您愿意使用其他允许更多流相关操作的“函数式”Java 库,您可以使用jOOL 及其接受可迭代参数的Seq.zip

    Seq.zip(keys, values, SimpleImmutableEntry::new)
        .collect(toImmutableListMultimap(Map.Entry::getKey, Map.Entry::getValue));

另一个库是 StreamEx,它公开了 EntryStream - 键值对流的抽象。

【讨论】:

    【解决方案2】:

    如果您的列表是随机访问的,则无需压缩即可:

    Map<Key, Value> map = IntStream.range(0, Math.min(keys.size(), values.size()))
        .boxed()
        .collect(toImmutableListMultimap(i -> keys[i], i -> values[i]));
    

    【讨论】:

    • 对于更简单的场景来说,这确实是一个合乎逻辑的解决方案。 :)
    • 同意,但也更传统。看起来像老派for(int i=0;i&lt;size;i++):P
    • @user1686407 是的! :D
    【解决方案3】:

    所以,你几乎做对了!下面是您更新的代码(假设您的列表是Integer 类型):

    Streams.zip(keys.stream(), values.stream(), AbstractMap.SimpleImmutableEntry::new)
                    .collect(toImmutableListMultimap(Map.Entry<Integer, Integer>::getKey,
                            Map.Entry<Integer, Integer>::getValue)
                    );
    

    但我总是喜欢在原生层面做事,因为它会给你更多的力量。使用collect查看下面的代码:

    Streams.zip(keys.stream(), values.stream(), (k, v) -> new AbstractMap.SimpleImmutableEntry(k, v))
                            .collect(ImmutableListMultimap::builder,
                                    ImmutableListMultimap.Builder::put,
                                    (builder2, builder3) -> builder2.putAll(builder3.build())
    
                            ).build();
    

    注意:- builder2.putAll(builder3.build()) BiConsumer 仅在您使用并行流时才有效。这就是collect(我最喜欢的流之一)的行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-04
      • 2015-08-04
      • 2012-09-23
      • 1970-01-01
      相关资源
      最近更新 更多