累积数值并在Java流中返回结果

huangapple go评论72阅读模式
英文:

Accumulate values and return result in Java stream

问题

我有一个包含Seed元素的类。Seed的一个方法返回类型是Optional<Pair<Boolean, Boolean>>

我正在尝试遍历所有的seeds,保持返回类型(Optional<Pair<Boolean, Boolean>>),但我想要能够判断是否至少有一个true值(在任何一个Pair中),并用它覆盖结果。基本上,如果集合是(跳过Optional包装以简化问题):[Pair<false, false>, Pair<false, true>, Pair<false, false>],我希望返回一个Pair<false, true>Optional,因为第二个元素为true。最终,我只关心是否有true值。

  public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
    return seeds.stream()
        .map(Seed::hadExposure)
        ...
  }

我尝试过使用reduce,但没有找到有用的方法。

我的问题直接与Java流相关。我可以轻松地使用for循环来实现这一点,但最初我打算使用流。

英文:

I have a class with a collection of Seed elements. One of the method's return type of Seed is Optional<Pair<Boolean, Boolean>>.

I'm trying to loop over all seeds, keeping the return type (Optional<Pair<Boolean, Boolean>>), but I would like to be able to say if there was at least true value (in any of the Pairs) and override the result with it. Basically, if the collection is (skipping the Optional wrapper to make things simpler): [Pair<false, false>, Pair<false, true>, Pair<false, false>] I would like to return and Optional of Pair<false, true> because the second element had true. In the end, I'm interested if there was a true value and that's about it.

  public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
    return seeds.stream()
        .map(Seed::hadExposure)
        ...
  }

I was playing with reduce but couldn't come up with anything useful.

> My question is related with Java streams directly. I can easily do this with a for loop, but I aimed initially for streams.

答案1

得分: 3

直接方法

由于您使用的是Java 11,您可以使用Optional::stream(在Java 9中引入)来摆脱Optional包装器。作为终端操作,reduce是您的朋友:

public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
	// 不管种子从哪里来
	Stream<Optional<Pair<Boolean, Boolean>>> seeds = seeds();
	return seeds
		.flatMap(Optional::stream)
		.reduce((pair1, pair2) -> new Pair<>(
			pair1.left() || pair2.left(),
			pair1.right() || pair2.right())
	);
}

扩展方法

如果您想更进一步,为您的Pair提供一种通用的方法,可以将另一个Pair与新实例一起折叠,您可以使代码更加表达:

public class Pair<LEFT, RIGHT> {

	private final LEFT left;
	private final RIGHT right;

	// 构造函数,equals,hashCode,toString,...

	public Pair<LEFT, RIGHT> fold(
			Pair<LEFT, RIGHT> other,
			BinaryOperator<LEFT> combineLeft,
			BinaryOperator<RIGHT> combineRight) {
		return new Pair<>(
			combineLeft.apply(left, other.left),
			combineRight.apply(right, other.right));
	}

}

// 现在您可以使用fold和Boolean::logicalOr
// https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Boolean.html#logicalOr(boolean,boolean)

public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
	Stream<Optional<Pair<Boolean, Boolean>>> seeds = seeds();
	return seeds
		.flatMap(Optional::stream)
		.reduce((pair1, pair2) -> pair1
			.fold(pair2, Boolean::logicalOr, Boolean::logicalOr))
	);
}

我可能不会仅为了这个用例创建Pair::fold,但我会动心尝试一下。 累积数值并在Java流中返回结果

英文:

Straighforward

Since you're Java 11, you can use Optional::stream (introduced in Java 9) to get rid of the Optional wrapper. As a terminal operation, reduce is your friend:

public Optional&lt;Pair&lt;Boolean, Boolean&gt;&gt; hadAnyExposure() {
	// wherever the seeds come from
	Stream&lt;Optional&lt;Pair&lt;Boolean, Boolean&gt;&gt;&gt; seeds = seeds();
	return seeds
		.flatMap(Optional::stream)
		.reduce((pair1, pair2) -&gt; new Pair&lt;&gt;(
			pair1.left() || pair2.left(),
			pair1.right() || pair2.right())
	);
}

Extended

If you want to go a step further and give your Pair a general way to be folded with another Pair into a new instance, you can make the code a bit more expressive:

public class Pair&lt;LEFT, RIGHT&gt; {

	private final LEFT left;
	private final RIGHT right;

	// constructor, equals, hashCode, toString, ...

	public Pair&lt;LEFT, RIGHT&gt; fold(
			Pair&lt;LEFT, RIGHT&gt; other,
			BinaryOperator&lt;LEFT&gt; combineLeft,
			BinaryOperator&lt;RIGHT&gt; combineRight) {
		return new Pair&lt;&gt;(
			combineLeft.apply(left, other.left),
			combineRight.apply(right, other.right));
	}

}

// now you can use fold and Boolean::logicalOr
// https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Boolean.html#logicalOr(boolean,boolean)

public Optional&lt;Pair&lt;Boolean, Boolean&gt;&gt; hadAnyExposure() {
	Stream&lt;Optional&lt;Pair&lt;Boolean, Boolean&gt;&gt;&gt; seeds = seeds();
	return seeds
		.flatMap(Optional::stream)
		.reduce((pair1, pair2) -&gt; pair1
			.fold(pair2, Boolean::logicalOr, Boolean::logicalOr))
	);
}

I probably wouldn't create Pair::fold just for this use case, but I would be tempted. 累积数值并在Java流中返回结果

答案2

得分: 1

你对于 reduce 的想法似乎是正确的,使用 || 来同时合并每个 Pair 的两侧(我不太确定你的 Optional 语义是什么,所以在这里我会筛选掉空值,这可能可以得到你想要的结果,但你可能需要调整):

Optional<Pair<Boolean, Boolean>> result = seeds.stream().map(Seed::hadExposure)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .reduce((a, b) -> new Pair<>(a.first || b.first, a.second || b.second));
英文:

Your thoughts on reduce look like the right way to go, using || to reduce both sides of each Pair together. (Not exactly sure what your Optional semantics are, so going to filter out empty ones here and that might get what you want, but you may need to adjust):

Optional&lt;Pair&lt;Boolean, Boolean&gt;&gt; result = seeds.stream().map(Seed::hadExposure)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .reduce((a, b) -&gt; new Pair&lt;&gt;(a.first || b.first, a.second || b.second));

答案3

得分: 1

根据您标记此问题为 java-11,您可以使用 Optional.stream 方法:

public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
    return Optional.of(
        seeds.stream()
             .flatMap(seed -> seed.hadExposure().stream())
             .collect(
                 () -> new Pair<Boolean, Boolean>(false, false),
                 (p, seed) -> { 
                     p.setLeft(p.getLeft() || seed.getLeft());
                     p.setRight(p.getRight() || seed.getRight()); 
                 },
                 (p1, p2) -> {
                     p1.setLeft(p1.getLeft() || p2.getLeft());
                     p1.setRight(p1.getRight() || p2.getRight());
                 }));
}

这首先通过 Optional.stream 方法消除了 Optional(只保留了成对的值),然后使用 Stream.collect 来通过 OR 关联 操作可变地减少这些成对的值。

注意:使用 Stream.reduce 也可以工作,但会创建许多不必要的中间成对的值。这就是我为什么改用 Stream.collect 的原因。

英文:

As you've tagged this question with java-11, you can make use of the Optional.stream method:

public Optional&lt;Pair&lt;Boolean, Boolean&gt;&gt; hadAnyExposure() {
    return Optional.of(
        seeds.stream()
             .flatMap(seed -&gt; seed.hadExposure().stream())
             .collect(
                 () -&gt; new Pair&lt;Boolean, Boolean&gt;(false, false),
                 (p, seed) -&gt; { 
                     p.setLeft(p.getLeft() || seed.getLeft());
                     p.setRight(p.getRight() || seed.getRight()); 
                 },
                 (p1, p2) -&gt; {
                     p1.setLeft(p1.getLeft() || p2.getLeft());
                     p1.setRight(p1.getRight() || p2.getRight());
                 }));
}

This first gets rid of the Optional by means of the Optional.stream method (keeping just the pairs) and then uses Stream.collect to mutably reduce the pairs by means of the OR associative operation.

Note: using Stream.reduce would also work, but it would create a lot of unnecessary intermediate pairs. That's why I've used Stream.collect instead.

答案4

得分: 0

使用Collectors.partitioningBy,您可以获得一个布尔键的Map,然后您可以轻松检索以键true为索引的值。

Optional<Pair<Boolean, Boolean>> collect = Arrays.asList(pair1, pair2, pair3).stream()
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect(Collectors.collectingAndThen(Collectors.partitioningBy(p -> p.getFirst() == true || p.getSecond() == true),
                m -> m.get(true).stream().findAny()));
英文:

using Collectors.partitioningBy you can get a Map with boolean keys after that you can easily retrieve values indexed with the key true

Optional&lt;Pair&lt;Boolean, Boolean&gt;&gt; collect = Arrays.asList(pair1, pair2, par3).stream()
            .filter(Optional::isPresent)
            .map(Optional::get)
            .collect(Collectors.collectingAndThen(Collectors.partitioningBy(p -&gt; p.getFirst() == true || p.getSecond() == true),
                    m -&gt; m.get(true).stream().findAny()));

huangapple
  • 本文由 发表于 2020年9月22日 01:13:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/63997035.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定