gpt4 book ai didi

Java 8 : Extracting a pair of arrays out of a Stream

转载 作者:塔克拉玛干 更新时间:2023-11-01 22:06:52 27 4
gpt4 key购买 nike

所以我有一些使用 Java 8 流的代码,而且它可以工作。它做的正是我需要它做的,而且清晰易读(这在函数式编程中很少见)。在子例程结束时,代码遍历自定义对类型的列表:

// All names Hungarian-Notation-ized for SO reading
class AFooAndABarWalkIntoABar
{
public int foo_int;
public BarClass bar_object;
....
}

List<AFooAndABarWalkIntoABar> results = ....;

这里的数据必须作为数组传递到程序的其他部分,所以它们被复制出来:

// extract either a foo or a bar from each "foo-and-bar" (fab)
int[] foo_array = results.stream()
.mapToInt (fab -> fab.foo_int)
.toArray();

BarClass[] bar_array = results.stream()
.map (fab -> fab.bar_object)
.toArray(BarClass[]::new);

完成了。现在每个阵列都可以去做它的事情了。

除了...在列表上循环两次让我心烦意乱。如果我们需要跟踪更多信息,他们可能会添加第三个字段,然后必须进行第三次传递以将 3 元组转换为三个数组,等等。所以我在开玩笑一次性完成。

分配数据结构很简单,但维护一个供消费者使用的索引似乎很可怕:

int[] foo_array = new int[results.size()];
BarClass[] bar_array = new BarClass[results.size()];

// the trick is providing a stateful iterator across the array:
// - can't just use 'int', it's not effectively final
// - an actual 'final int' would be hilariously wrong
// - "all problems can be solved with a level of indirection"
class Indirection { int iterating = 0; }
final Indirection sigh = new Indirection();
// equivalent possibility is
// final int[] disgusting = new int[]{ 0 };
// and then access disgusting[0] inside the lambda
// wash your hands after typing that code

results.stream().forEach (fab -> {
foo_array[sigh.iterating] = fab.foo_int;
bar_array[sigh.iterating] = fab.bar_object;
sigh.iterating++;
});

这会生成与使用多个流循环的现有解决方案相同的数组。它在大约一半的时间内这样做,去计算。但是迭代器间接技巧看起来非常丑陋,当然排除了并行填充数组的任何可能性。

使用一对以适当容量创建的 ArrayList 实例,将使消费者代码只需为每个实例调用 add,而无需外部迭代器。但是 ArrayList 的 toArray(T[]) 必须再次执行存储数组的副本,并且在 int 的情况下,还有在此之上的装箱/拆箱。

(编辑:“可能重复”问题的答案都谈到只维护流中的索引,并在 filter/ 期间使用直接数组索引获取实际数据map 调用,并注意如果数据不能通过直接索引访问,它实际上不起作用。虽然这个问题有一个 List 并且只能从“直接索引” “嗯,从技术上讲,List#get 存在”的观点。例如,如果上面的结果集合是一个 LinkedList,则调用 O(n) get N 次不连续的索引会……不好。)

还有其他更好的可能性是我遗漏的吗?我认为自定义 Collector 可能会做到这一点,但我也不知道如何在那里维护状态,甚至从来没有得到过临时代码。

最佳答案

由于流的大小是已知的,因此没有理由再次重新发明轮子。最简单的解决方案通常是最好的解决方案。您展示的第二种方法几乎就绪 - 只需使用 AtomicInteger 作为数组索引,您将实现您的目标 - 单次传递数据,以及可能的并行流执行(由于 AtomicInteger).

所以

AtomicInteger index=new AtomicInteger()
results.parallelStream().forEach (fab -> {
int idx=index.getAndIncrement();
foo_array[idx] = fab.foo_int;
bar_array[idx] = fab.bar_object;
});

并行执行的线程安全。对整个集合进行一次迭代

关于Java 8 : Extracting a pair of arrays out of a Stream<Pair>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41926920/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com