使用 Guava 将两个列表压缩到 Java 8 中的不可变多映射中?

Zip two lists into an immutable multimap in Java 8 with Guava?

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

番石榴/Java8 可能的第一步是

Streams.zip(keys, values, zippingFunction)

我认为 zippingFunction 需要 return 地图条目,但没有可公开构建的列表条目。所以我能写的 "most" 函数式方法是使用一个压缩函数 return 是一个 Pair,我不确定它是否存在于 Guava 中,或者 return 是一个双元素列表,它是一个可变类型,不能正确表示恰好有 2 个元素。

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

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

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

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

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

如果您真的想使用流来 "be functional",压缩两个流是可行的方法,但您仍然必须在收集到多图之前创建中间 "pair" 对象。您声称 "there isn't a publicly constructable list entry",但事实并非如此,您可以在此处使用 JDK 的 SimpleImmutableEntry and Guava's Maps.immutableEntry(它们比更通用的 Pair 更合适,实际上, 在 JDK 或 Guava 中都找不到。

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

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

如果您愿意使用其他 "functional" Java 允许更多 stream-related 操作的库,您可以使用 jOOL and its Seq.zip,它接受可迭代的参数:

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

另一个库是 StreamEx, which exposes EntryStream - key-value 对流的抽象。

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

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

所以,你几乎做对了!以下是您更新后的代码(假设您的列表是 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(我最喜欢的流之一)的行为。