向流中添加两个 Java 8 流或一个额外的元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22740464/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Adding two Java 8 streams, or an extra element to a stream
提问by MarcG
I can add streams or extra elements, like this:
我可以添加流或额外元素,如下所示:
Stream stream = Stream.concat(stream1, Stream.concat(stream2, Stream.of(element));
And I can add new stuff as I go, like this:
我可以随时添加新内容,如下所示:
Stream stream = Stream.concat(
Stream.concat(
stream1.filter(x -> x!=0), stream2)
.filter(x -> x!=1),
Stream.of(element))
.filter(x -> x!=2);
But this is ugly, because concat
is static. If concat
were an instance method, the above examples would be much easier to read:
但这很丑陋,因为它concat
是静态的。如果concat
是一个实例方法,上面的例子会更容易阅读:
Stream stream = stream1.concat(stream2).concat(element);
And
和
Stream stream = stream1
.filter(x -> x!=0)
.concat(stream2)
.filter(x -> x!=1)
.concat(element)
.filter(x -> x!=2);
My question is:
我的问题是:
1) Is there any good reason why concat
is static? Or is there some equivalent instance method I'm missing?
1)有什么好的理由为什么concat
是静态的?或者是否有一些我缺少的等效实例方法?
2) In any case, is there a better way of doing this?
2)无论如何,有没有更好的方法来做到这一点?
采纳答案by nosid
If you add static importsfor Stream.concatand Stream.of, the first example could be written as follows:
如果你为Stream.concat和Stream.of添加静态导入,第一个例子可以写成如下:
Stream<Foo> stream = concat(stream1, concat(stream2, of(element)));
Importing static methodswith generic names can result in code that becomes difficult to read and maintain (namespace pollution). So, it might be better to create your own static methodswith more meaningful names. However, for demonstration I will stick with this name.
导入具有泛型名称的静态方法会导致代码变得难以阅读和维护(命名空间污染)。因此,使用更有意义的名称创建自己的静态方法可能会更好。但是,为了演示,我会坚持使用这个名字。
public static <T> Stream<T> concat(Stream<? extends T> lhs, Stream<? extends T> rhs) {
return Stream.concat(lhs, rhs);
}
public static <T> Stream<T> concat(Stream<? extends T> lhs, T rhs) {
return Stream.concat(lhs, Stream.of(rhs));
}
With these two static methods (optionally in combination with static imports), the two examples could be written as follows:
使用这两个静态方法(可以选择与静态导入结合使用),这两个示例可以写成如下:
Stream<Foo> stream = concat(stream1, concat(stream2, element));
Stream<Foo> stream = concat(
concat(stream1.filter(x -> x!=0), stream2).filter(x -> x!=1),
element)
.filter(x -> x!=2);
The code is now significantly shorter. However, I agree that the readability hasn't improved. So I have another solution.
代码现在明显缩短了。但是,我同意可读性没有提高。所以我有另一个解决方案。
In a lot of situations, Collectorscan be used to extendthe functionality of streams. With the two Collectorsat the bottom, the two examples could be written as follows:
在很多情况下,收集器可用于扩展流的功能。使用底部的两个收集器,这两个示例可以写成如下:
Stream<Foo> stream = stream1.collect(concat(stream2)).collect(concat(element));
Stream<Foo> stream = stream1
.filter(x -> x!=0)
.collect(concat(stream2))
.filter(x -> x!=1)
.collect(concat(element))
.filter(x -> x!=2);
The only difference between your desired syntax and the syntax above is, that you have to replace concat(...)with collect(concat(...)). The two static methods can be implemented as follows (optionally used in combination with static imports):
您所需的语法与上述语法之间的唯一区别是,您必须将concat(...)替换为collect(concat(...))。这两个静态方法可以实现如下(可选与静态导入结合使用):
private static <T,A,R,S> Collector<T,?,S> combine(Collector<T,A,R> collector, Function<? super R, ? extends S> function) {
return Collector.of(
collector.supplier(),
collector.accumulator(),
collector.combiner(),
collector.finisher().andThen(function));
}
public static <T> Collector<T,?,Stream<T>> concat(Stream<? extends T> other) {
return combine(Collectors.toList(),
list -> Stream.concat(list.stream(), other));
}
public static <T> Collector<T,?,Stream<T>> concat(T element) {
return concat(Stream.of(element));
}
Of course there is a drawback with this solution that should be mentioned. collectis a final operation that consumes all elements of the stream. On top of that, the collector concatcreates an intermediate ArrayListeach time it is used in the chain. Both operations can have a significant impact on the behaviour of your program. However, if readabilityis more important than performance, it might still be a very helpful approach.
当然,这个解决方案有一个缺点,应该提到。collect是消耗流中所有元素的最终操作。最重要的是,收集器concat每次在链中使用时都会创建一个中间ArrayList。这两种操作都会对程序的行为产生重大影响。然而,如果可读性比性能更重要,它可能仍然是一个非常有用的方法。
回答by Edwin Dalorzo
Unfortunately this answer is probably of little or no help whatsoever, but I did a forensics analysis of the Java Lambda Mailing list to see if I could find the cause of this design. This is what I found out.
不幸的是,这个答案可能没有任何帮助,但我对 Java Lambda 邮件列表进行了取证分析,看看我是否能找到这种设计的原因。这是我发现的。
In the beginning there was an instance method for Stream.concat(Stream)
一开始有 Stream.concat(Stream) 的实例方法
In the mailing list I can clearly see the method was originally implemented as an instance method, as you can read in this threadby Paul Sandoz, about the concat operation.
在邮件列表中,我可以清楚地看到该方法最初是作为实例方法实现的,您可以在Paul Sandoz 的这个线程中阅读有关 concat 操作的内容。
In it they discuss the issues that could arise from those cases in which the stream could be infinite and what concatenation would mean in those cases, but I do not think that was the reason for the modification.
在其中,他们讨论了流可能是无限的情况下可能出现的问题以及在这些情况下串联意味着什么,但我认为这不是修改的原因。
You see in this other threadthat some early users of the JDK 8 questioned about the behavior of the concat instance method when used with null arguments.
您在另一个线程中看到,一些 JDK 8 的早期用户质疑 concat 实例方法在与空参数一起使用时的行为。
This other threadreveals, though, that the design of the concat method was under discussion.
这个其他线程显示,虽然,concat的设计是正在讨论中。
Refactored to Streams.concat(Stream,Stream)
重构为 Streams.concat(Stream,Stream)
But without any explanation, suddenly, the methods were changed to static methods, as you can see in this thread about combining streams. This is perhaps the only mail thread that sheds a bit of light about this change, but it was not clear enough for me to determine the reason for the refactoring. But we can see they did a commitin which they suggested to move the concat
method out of Stream
and into the helper class Streams
.
但是没有任何解释,突然,这些方法更改为静态方法,正如您在有关组合流的线程中所见。这可能是唯一对这一变化有所了解的邮件线程,但我还不够清楚,无法确定重构的原因。但是我们可以看到他们做了一个提交,其中他们建议将concat
方法移出Stream
和移入 helper 类Streams
。
Refactored to Stream.concat(Stream,Stream)
重构为 Stream.concat(Stream,Stream)
Later, it was moved againfrom Streams
to Stream
, but yet again, no explanation for that.
后来,它再次从Streams
移到Stream
,但又一次,没有解释。
So, bottom line, the reason for the design is not entirely clear for me and I could not find a good explanation. I guess you could still ask the question in the mailing list.
所以,归根结底,设计的原因对我来说并不完全清楚,我找不到很好的解释。我想你仍然可以在邮件列表中提出这个问题。
Some Alternatives for Stream Concatenation
流连接的一些替代方案
This other thread by Michael Hixsondiscusses/asks about other ways to combine/concat streams
这其他线程由迈克尔·希克森讨论/询问等方式来结合/ CONCAT流
To combine two streams, I should do this:
Stream.concat(s1, s2)
not this:
Stream.of(s1, s2).flatMap(x -> x)
... right?
To combine more than two streams, I should do this:
Stream.of(s1, s2, s3, ...).flatMap(x -> x)
not this:
Stream.of(s1, s2, s3, ...).reduce(Stream.empty(), Stream::concat)
... right?
要合并两个流,我应该这样做:
Stream.concat(s1, s2)
不是这个:
Stream.of(s1, s2).flatMap(x -> x)
... 对?
要合并两个以上的流,我应该这样做:
Stream.of(s1, s2, s3, ...).flatMap(x -> x)
不是这个:
Stream.of(s1, s2, s3, ...).reduce(Stream.empty(), Stream::concat)
... 对?
回答by Tagir Valeev
My StreamExlibrary extends the functionality of Stream API. In particular it offers methods like appendand prependwhich solve this issue (internally they use concat
). These methods can accept either another stream or collection or varargs array. Using my library your problem can be solved this way (note that x != 0
look strange for non-primitive stream):
我的StreamEx库扩展了 Stream API 的功能。特别是它提供了诸如append和prepend 之类的方法来解决这个问题(他们在内部使用concat
)。这些方法可以接受另一个流或集合或可变参数数组。使用我的库,您的问题可以通过这种方式解决(请注意,x != 0
非原始流看起来很奇怪):
Stream<Integer> stream = StreamEx.of(stream1)
.filter(x -> !x.equals(0))
.append(stream2)
.filter(x -> !x.equals(1))
.append(element)
.filter(x -> !x.equals(2));
By the way there's also a shortcut for your filter
operation:
顺便说一句,您的filter
操作还有一个快捷方式:
Stream<Integer> stream = StreamEx.of(stream1).without(0)
.append(stream2).without(1)
.append(element).without(2);
回答by herman
Just do:
做就是了:
Stream.of(stream1, stream2, Stream.of(element)).flatMap(identity());
where identity()
is a static import of Function.identity()
.
identity()
的静态导入在哪里Function.identity()
?
Concatenating multiple streams into one stream is the same as flattening a stream.
将多个流连接成一个流与展平流相同。
However, unfortunately, for some reason there is no flatten()
method on Stream
, so you have to use flatMap()
with the identity function.
但是,不幸的是,由于某种原因,没有flatten()
方法 on Stream
,因此您必须使用flatMap()
identity 函数。
回答by John McClean
If you don't mind using 3rd Party Libraries cyclops-reacthas an extended Stream type that will allow you to do just that via the append / prepend operators.
如果你不介意使用 3rd Party Libraries,那么cyclops-react有一个扩展的 Stream 类型,它允许你通过 append / prepend 运算符来做到这一点。
Individual values, arrays, iterables, Streams or reactive-streams Publishers can be appended and prepended as instance methods.
单个值、数组、可迭代对象、流或反应流发布者可以作为实例方法附加和前置。
Stream stream = ReactiveSeq.of(1,2)
.filter(x -> x!=0)
.append(ReactiveSeq.of(3,4))
.filter(x -> x!=1)
.append(5)
.filter(x -> x!=2);
[Disclosure I am the lead developer of cyclops-react]
[披露我是独眼巨人反应的首席开发人员]
回答by Felix S
How about writing your own concat method?
编写自己的 concat 方法怎么样?
public static Stream<T> concat(Stream<? extends T> a,
Stream<? extends T> b,
Stream<? extends T> args)
{
Stream<T> concatenated = Stream.concat(a, b);
for (Stream<T> stream : args)
{
concatenated = Stream.concat(concatenated, stream);
}
return concatenated;
}
This at least makes your first example a lot more readable.
这至少使您的第一个示例更具可读性。
回答by Kunda
You can use Guava's Streams
.
concat(Stream<? extends T>... streams)
method, which will be very short with static imports:
您可以使用 Guava 的方法,该方法对于静态导入将非常简短:Streams
.
concat(Stream<? extends T>... streams)
Stream stream = concat(stream1, stream2, of(element));
回答by Legna
At the end of the day I'm not interested in combining streams, but in obtaining the combined result of processing each element of all those streams.
归根结底,我对组合流不感兴趣,但对获得处理所有这些流的每个元素的组合结果不感兴趣。
While combining streams might prove to be cumbersome (thus this thread), combining their processing results is fairly easy.
虽然合并流可能被证明是麻烦的(因此这个线程),合并它们的处理结果是相当容易的。
The key to solve is to create your own collector and ensure that the supplier function for the new collector returns the samecollection every time (not a new one), the code below illustrates this approach.
解决的关键是创建自己的收集器并确保新收集器的供应商函数每次返回相同的集合(不是新的),下面的代码说明了这种方法。
package scratchpad;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Stream;
public class CombineStreams {
public CombineStreams() {
super();
}
public static void main(String[] args) {
List<String> resultList = new ArrayList<>();
Collector<String, List<String>, List<String>> collector = Collector.of(
() -> resultList,
(list, item) -> {
list.add(item);
},
(llist, rlist) -> {
llist.addAll(rlist);
return llist;
}
);
String searchString = "Wil";
System.out.println("After processing first stream\n"
+ createFirstStream().filter(name -> name.contains(searchString)).collect(collector));
System.out.println();
System.out.println("After processing second stream\n"
+ createSecondStream().filter(name -> name.contains(searchString)).collect(collector));
System.out.println();
System.out.println("After processing third stream\n"
+ createThirdStream().filter(name -> name.contains(searchString)).collect(collector));
System.out.println();
}
private static Stream<String> createFirstStream() {
return Arrays.asList(
"William Shakespeare",
"Emily Dickinson",
"H. P. Lovecraft",
"Arthur Conan Doyle",
"Leo Tolstoy",
"Edgar Allan Poe",
"Robert Ervin Howard",
"Rabindranath Tagore",
"Rudyard Kipling",
"Seneca",
"John Donne",
"Sarah Williams",
"Oscar Wilde",
"Catullus",
"Alfred Tennyson",
"William Blake",
"Charles Dickens",
"John Keats",
"Theodor Herzl"
).stream();
}
private static Stream<String> createSecondStream() {
return Arrays.asList(
"Percy Bysshe Shelley",
"Ernest Hemingway",
"Barack Obama",
"Anton Chekhov",
"Henry Wadsworth Longfellow",
"Arthur Schopenhauer",
"Jacob De Haas",
"George Gordon Byron",
"Hyman London",
"Robert Frost",
"Abraham Lincoln",
"O. Henry",
"Ovid",
"Robert Louis Stevenson",
"John Masefield",
"James Joyce",
"Clark Ashton Smith",
"Aristotle",
"William Wordsworth",
"Jane Austen"
).stream();
}
private static Stream<String> createThirdStream() {
return Arrays.asList(
"Niccolò Machiavelli",
"Lewis Carroll",
"Robert Burns",
"Edgar Rice Burroughs",
"Plato",
"John Milton",
"Ralph Waldo Emerson",
"Margaret Thatcher",
"Sylvie d'Avigdor",
"Marcus Tullius Cicero",
"Banjo Paterson",
"Woodrow Wilson",
"Walt Whitman",
"Theodore Roosevelt",
"Agatha Christie",
"Ambrose Bierce",
"Nikola Tesla",
"Franz Kafka"
).stream();
}
}