Java 8收集器适用于Guava不可变表

3

使用场景:
通过返回类型为{R,C,V}ImmutableTable of {Integer,String,Boolean} process(String item){...}方法处理字符串列表。

收集结果,即合并所有结果并返回ImmutableTable。是否有实现方法?

当前的实现方法(由Bohemian建议):

使用并行流如何?下面的代码是否存在任何并发问题?使用并行流时,在tableBuilder.build()处出现"NullPointerException at index 1800",但在流中运行正常。

ImmutableTable<Integer, String, Boolean> buildData() {   

  // list of 4 AwsS3KeyName   
listToProcess.parallelStream() 

  //Create new instance via Guice dependency injection 
.map(s3KeyName -> ProcessorInstanceProvider.get()    
.fetchAndBuild(s3KeyName)) 
.forEach(tableBuilder::putAll); 

 return tableBuilder.build(); }

以下代码可以使用流和并行流很好地工作。但是由于行和列的重复条目,ImmutableBuild失败了。在合并表格时,防止重复的最佳方法是什么?

public static <R, C, V> Collector<ImmutableTable<R, C, V>,     
ImmutableTable.Builder<R, C, V>, ImmutableTable<R, C, V>>   
toImmutableTable() 
{ 
return Collector.of(ImmutableTable.Builder::new, 
ImmutableTable.Builder::putAll, (builder1, builder2) -> 
builder1.putAll(builder2.build()), ImmutableTable.Builder::build); }

编辑: 如果在合并不同的表时,ImmutableTable.Builder 中存在任何重复条目,则会失败。

为了避免失败,尝试将 ImmutableTables 放入 HashBasedTable 中。

  ImmutableTable.copyOf(itemListToProcess.parallelStream()
            .map(itemString ->
           ProcessorInstanceProvider.get()
                    .buildImmutableTable(itemString))
                    .collect(
                            Collector.of(
                                    HashBasedTable::create,
                                    HashBasedTable::putAll,
                                    (a, b) -> {
                                        a.putAll(b);
                                        return a;
                                    }));
  )

但是我遇到了运行时异常“Caused by: java.lang.IllegalAccessError: tried to access class com.google.common.collect.AbstractTable”。

我们如何使用HashBasedTable作为累加器来收集ImmutablesTables,由于HashBasedTable会用最新的条目覆盖现有条目,而且如果我们尝试放置重复的条目,则不会失败,并返回聚合的Immutable表。


3
你能展示一下你无法编写正确的代码吗? - Paul Rooney
3个回答

8
自Guava 21以来,您可以使用ImmutableTable.toImmutableTable收集器。
public ImmutableTable<Integer, String, Boolean> processList(List<String> strings) {
    return strings.stream()
            .map(this::processText)
            .flatMap(table -> table.cellSet().stream())
            .collect(ImmutableTable.toImmutableTable(
                    Table.Cell::getRowKey,
                    Table.Cell::getColumnKey,
                    Table.Cell::getValue,
                    (b1, b2) -> b1 && b2 // You can ommit merge function!
            ));
}

private ImmutableTable<Integer, String, Boolean> processText(String text) {
    return ImmutableTable.of(); // Whatever
}

3
这应该可以工作:
List<String> list; // given a list of String

ImmutableTable result = list.parallelStream()
    .map(processor::process) // converts String to ImmutableTable
    .collect(ImmutableTable.Builder::new, ImmutableTable.Builder::putAll,
        (a, b) -> a.putAll(b.build())
    .build();

这个缩减操作是线程安全的。


或者使用 HashBasedTable 作为中间数据结构:

ImmutableTable result = ImmutableTable.copyOf(list.parallelStream()
    .map(processor::process) // converts String to ImmutableTable
    .collect(HashBasedTable::create, HashBasedTable::putAll, HashBasedTable::putAll));

使用并行流怎么样?你在这里看到任何并发问题吗?public ImmutableTable<Integer, String, Boolean> fetch() { listToProcess.parallelStream() // 4个AwsS3KeyName的列表 .map(s3KeyName ->
ProcessorInstanceProvider.get() //通过Guice依赖注入创建新实例 .build(s3KeyName)) .forEach(tableBuilder::putAll); return tableBuilder.build(); }
- sidss
1
文档没有说明ImmutableTable是线程安全的,但请参见已更改的代码,该代码是线程安全的(而且现在只有一行 :) )。 - Bohemian
非常感谢您提供这个解决方案。由于重复项,构建失败了,请告诉我如何防止重复项?看来我需要使用HashBasedTable。 - sidss
1
@sidss 复制什么? - Bohemian
当两个不同的表中存在相同的行列条目时。所以当调用ImmutableTable.build并且存在任何重复时,它会失败。所以似乎我需要使用HashBasedTable而不是ImmutableTable.Builder。 - sidss
我的方法是,不使用Immutable table builder,而是在HashBasedTable上执行putAll操作,然后返回ImmutableTable.copyOf(hashBasedTable)。 - sidss

3

您可以通过创建一个合适的Collector并使用Collector.of静态工厂方法来实现此操作:

ImmutableTable<R, C, V> table =
    list.stream()
        .map(processor::process)
        .collect(
            Collector.of(
                () -> new ImmutableTable.Builder<R, C, V>(),
                (builder, table1) -> builder.putAll(table1),
                (builder1, builder2) ->
                    new ImmutableTable.Builder<R, C, V>()
                        .putAll(builder1.build())
                        .putAll(builder2.build()),
                ImmutableTable.Builder::build));

我认为你可以使用方法引用来处理Supplier(ImmutableTable.Builder::new)和BiConsumer (ImmutableTable.Builder::putAll)。 - srborlongan
1
我不确定你能否做到:我尝试使用ImmutableTable.Builder::new,但它无法推断类型。 - Andy Turner
1
合并器可以通过重复使用其中一个构建器进行优化。例如:builder1.putAll(builder2.build()) - mfulton26
2
此外,如果您将所有内容都包装到一个函数中,您可以使用方法引用来作为提供者和累加器。例如:public static <R, C, V> Collector<ImmutableTable<R, C, V>, ImmutableTable.Builder<R, C, V>, ImmutableTable<R, C, V>> toImmutableTable() { return Collector.of(ImmutableTable.Builder::new, ImmutableTable.Builder::putAll, (builder1, builder2) -> builder1.putAll(builder2.build()), ImmutableTable.Builder::build); } - mfulton26
可以与流和并行流一起使用,效果非常好。但是由于行和列的重复条目,ImmutableBuild失败了。看来我必须使用HashBasedTable来防止重复。还有其他方法可以防止Immutable.copyof(hashBasedTable)吗? - sidss

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