How to interleave (merge) two Java 8 Streams?
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
What do I need to do for the output to be the below?
// one
// two
// three
// four
// five
// six
I looked into concat
but as the javadoc explains, it just appends one after the other, it does not interleave / intersperse.
Stream<String> out = Stream.concat(a, b);
out.forEach(System.out::println);
Creates a lazily concatenated stream whose elements are all the
elements of the first stream followed by all the elements of the
second stream.
Wrongly gives
// one
// three
// five
// two
// four
// six
Could do it if I collected them and iterated, but was hoping for something more Java8-y, Streamy :-)
Note
I don't want to zip the streams
“zip” operation will take an element from each collection and combine them.
the result of a zip operation would be something like this: (unwanted)
// onetwo
// threefour
// fivesix
java-8 functional-programming java-stream
|
show 2 more comments
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
What do I need to do for the output to be the below?
// one
// two
// three
// four
// five
// six
I looked into concat
but as the javadoc explains, it just appends one after the other, it does not interleave / intersperse.
Stream<String> out = Stream.concat(a, b);
out.forEach(System.out::println);
Creates a lazily concatenated stream whose elements are all the
elements of the first stream followed by all the elements of the
second stream.
Wrongly gives
// one
// three
// five
// two
// four
// six
Could do it if I collected them and iterated, but was hoping for something more Java8-y, Streamy :-)
Note
I don't want to zip the streams
“zip” operation will take an element from each collection and combine them.
the result of a zip operation would be something like this: (unwanted)
// onetwo
// threefour
// fivesix
java-8 functional-programming java-stream
zip is used to combine elements, I don't want to combine the elements, I want to keep the same total number of elements
– Blundell
Nov 14 '18 at 19:53
1
why wouldn't zip keep the same total number of elements?
– Aomine
Nov 14 '18 at 19:56
Reading the other thread, zip always takes a zipper function to combine an element from each stream to make a new element. I just want to interleave not zip
– Blundell
Nov 14 '18 at 19:56
2
For anyone coming here in the future, here is the comments + redirected answer : gist.github.com/blundell/3f062b8ec55fd1906c68e6ec8d848683
– Blundell
Nov 14 '18 at 20:13
1
I like the creation of theinterleave
method which essentially wraps thezip
method to improve readability et al. I've voted to reopen, so you could post that here instead of externally...
– Aomine
Nov 14 '18 at 20:17
|
show 2 more comments
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
What do I need to do for the output to be the below?
// one
// two
// three
// four
// five
// six
I looked into concat
but as the javadoc explains, it just appends one after the other, it does not interleave / intersperse.
Stream<String> out = Stream.concat(a, b);
out.forEach(System.out::println);
Creates a lazily concatenated stream whose elements are all the
elements of the first stream followed by all the elements of the
second stream.
Wrongly gives
// one
// three
// five
// two
// four
// six
Could do it if I collected them and iterated, but was hoping for something more Java8-y, Streamy :-)
Note
I don't want to zip the streams
“zip” operation will take an element from each collection and combine them.
the result of a zip operation would be something like this: (unwanted)
// onetwo
// threefour
// fivesix
java-8 functional-programming java-stream
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
What do I need to do for the output to be the below?
// one
// two
// three
// four
// five
// six
I looked into concat
but as the javadoc explains, it just appends one after the other, it does not interleave / intersperse.
Stream<String> out = Stream.concat(a, b);
out.forEach(System.out::println);
Creates a lazily concatenated stream whose elements are all the
elements of the first stream followed by all the elements of the
second stream.
Wrongly gives
// one
// three
// five
// two
// four
// six
Could do it if I collected them and iterated, but was hoping for something more Java8-y, Streamy :-)
Note
I don't want to zip the streams
“zip” operation will take an element from each collection and combine them.
the result of a zip operation would be something like this: (unwanted)
// onetwo
// threefour
// fivesix
java-8 functional-programming java-stream
java-8 functional-programming java-stream
edited Nov 14 '18 at 20:01
Blundell
asked Nov 14 '18 at 19:42
BlundellBlundell
56.9k27161193
56.9k27161193
zip is used to combine elements, I don't want to combine the elements, I want to keep the same total number of elements
– Blundell
Nov 14 '18 at 19:53
1
why wouldn't zip keep the same total number of elements?
– Aomine
Nov 14 '18 at 19:56
Reading the other thread, zip always takes a zipper function to combine an element from each stream to make a new element. I just want to interleave not zip
– Blundell
Nov 14 '18 at 19:56
2
For anyone coming here in the future, here is the comments + redirected answer : gist.github.com/blundell/3f062b8ec55fd1906c68e6ec8d848683
– Blundell
Nov 14 '18 at 20:13
1
I like the creation of theinterleave
method which essentially wraps thezip
method to improve readability et al. I've voted to reopen, so you could post that here instead of externally...
– Aomine
Nov 14 '18 at 20:17
|
show 2 more comments
zip is used to combine elements, I don't want to combine the elements, I want to keep the same total number of elements
– Blundell
Nov 14 '18 at 19:53
1
why wouldn't zip keep the same total number of elements?
– Aomine
Nov 14 '18 at 19:56
Reading the other thread, zip always takes a zipper function to combine an element from each stream to make a new element. I just want to interleave not zip
– Blundell
Nov 14 '18 at 19:56
2
For anyone coming here in the future, here is the comments + redirected answer : gist.github.com/blundell/3f062b8ec55fd1906c68e6ec8d848683
– Blundell
Nov 14 '18 at 20:13
1
I like the creation of theinterleave
method which essentially wraps thezip
method to improve readability et al. I've voted to reopen, so you could post that here instead of externally...
– Aomine
Nov 14 '18 at 20:17
zip is used to combine elements, I don't want to combine the elements, I want to keep the same total number of elements
– Blundell
Nov 14 '18 at 19:53
zip is used to combine elements, I don't want to combine the elements, I want to keep the same total number of elements
– Blundell
Nov 14 '18 at 19:53
1
1
why wouldn't zip keep the same total number of elements?
– Aomine
Nov 14 '18 at 19:56
why wouldn't zip keep the same total number of elements?
– Aomine
Nov 14 '18 at 19:56
Reading the other thread, zip always takes a zipper function to combine an element from each stream to make a new element. I just want to interleave not zip
– Blundell
Nov 14 '18 at 19:56
Reading the other thread, zip always takes a zipper function to combine an element from each stream to make a new element. I just want to interleave not zip
– Blundell
Nov 14 '18 at 19:56
2
2
For anyone coming here in the future, here is the comments + redirected answer : gist.github.com/blundell/3f062b8ec55fd1906c68e6ec8d848683
– Blundell
Nov 14 '18 at 20:13
For anyone coming here in the future, here is the comments + redirected answer : gist.github.com/blundell/3f062b8ec55fd1906c68e6ec8d848683
– Blundell
Nov 14 '18 at 20:13
1
1
I like the creation of the
interleave
method which essentially wraps the zip
method to improve readability et al. I've voted to reopen, so you could post that here instead of externally...– Aomine
Nov 14 '18 at 20:17
I like the creation of the
interleave
method which essentially wraps the zip
method to improve readability et al. I've voted to reopen, so you could post that here instead of externally...– Aomine
Nov 14 '18 at 20:17
|
show 2 more comments
5 Answers
5
active
oldest
votes
I’d use something like this:
public static <T> Stream<T> interleave(Stream<T> a, Stream<T> b) {
Spliterator<T> spA = a.spliterator(), spB = b.spliterator();
long s = spA.estimateSize() + spB.estimateSize();
if(s < 0) s = Long.MAX_VALUE;
int ch = spA.characteristics() & spB.characteristics()
& (Spliterator.NONNULL|Spliterator.SIZED);
ch |= Spliterator.ORDERED;
return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(s, ch) {
Spliterator<T> sp1 = spA, sp2 = spB;
@Override
public boolean tryAdvance(Consumer<? super T> action) {
Spliterator<T> sp = sp1;
if(sp.tryAdvance(action)) {
sp1 = sp2;
sp2 = sp;
return true;
}
return sp2.tryAdvance(action);
}
}, false);
}
It retains the characteristics of the input streams as far as possible, which allows certain optimizations (e.g. for count()
and toArray()
). Further, it adds the ORDERED
even when the input streams might be unordered, to reflect the interleaving.
When one stream has more elements than the other, the remaining elements will appear at the end.
add a comment |
A much dumber solution than Holger did, but may be it would fit your requirements:
private static <T> Stream<T> interleave(Stream<T> left, Stream<T> right) {
Spliterator<T> splLeft = left.spliterator();
Spliterator<T> splRight = right.spliterator();
T single = (T) new Object[1];
Stream.Builder<T> builder = Stream.builder();
while (splRight.tryAdvance(x -> single[0] = x) && splLeft.tryAdvance(builder)) {
builder.add(single[0]);
}
return builder.build();
}
This inconsistently includes all elements ofleft
, when it has more elements thanright
, but will drop elements ofright
when it has more thanleft
. You should decide. To included all common elements, usedo {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
, then decide. If you want to include all elements when the the streams have different sizes, do(splLeft.tryAdvance(builder)? splLeft: spRight).forEachRemaining(builder);
after the loop. And well,Stream.Builder<T>
does already implementConsumer<T>
, conveniently.
– Holger
Nov 15 '18 at 8:26
@Holger indeed i wanted only common ones, but now the problem is worse, sincedo {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
will still take an element fromleft
, so it's still not correct :(. the bigger problem I see now that I think about it - is that this is cheating,Stream.Builder
still uses a hidden collection to gather elements...
– Eugene
Nov 15 '18 at 9:16
Taking one additional element from left wouldn’t contradict the “interleaving” pattern (ababa). If you don’t want that, consider tracking the count and apply alimit
to the resulting stream. That’s simpler than dealing with an additional storage operation, especially with generic arrays. The builder implies a storage, I thought you were aware of it, as that’s the main (only) disadvantage over implementing a spliterator.
– Holger
Nov 15 '18 at 9:44
@Holger I thought about alimit
too, but don't know, the more I think about it, the more I like what you did
– Eugene
Nov 15 '18 at 9:46
1
@FedericoPeraltaSchaffner right, that is the exact point about the in memory collection, overall, just use whatever Holger put in place...
– Eugene
Nov 15 '18 at 14:44
|
show 5 more comments
As you can see from the question comments, I gave this a go using zip:
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
Stream<String> out = interleave(a, b);
public static <T> Stream<T> interleave(Stream<T> streamA, Stream<T> streamB) {
return zip(streamA, streamB, (o1, o2) -> Stream.of(o1, o2)).flatMap(s -> s);
}
/**
* https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip
**/
private static <A, B, C> Stream<C> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
final Iterator<A> iteratorA = streamA.iterator();
final Iterator<B> iteratorB = streamB.iterator();
final Iterator<C> iteratorC = new Iterator<C>() {
@Override
public boolean hasNext() {
return iteratorA.hasNext() && iteratorB.hasNext();
}
@Override
public C next() {
return zipper.apply(iteratorA.next(), iteratorB.next());
}
};
final boolean parallel = streamA.isParallel() || streamB.isParallel();
return iteratorToFiniteStream(iteratorC, parallel);
}
private static <T> Stream<T> iteratorToFiniteStream(Iterator<T> iterator, boolean parallel) {
final Iterable<T> iterable = () -> iterator;
return StreamSupport.stream(iterable.spliterator(), parallel);
}
add a comment |
This may not be a good answer because
(1) it collects to map, which you don't want to do I guess and
(2) it is not completely stateless as it uses AtomicIntegers.
Still adding it because
(1) it is readable and
(2) community can get an idea from this and try to improve it.
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
AtomicInteger i = new AtomicInteger(0);
AtomicInteger j = new AtomicInteger(1);
Stream.of(a.collect(Collectors.toMap(o -> i.addAndGet(2), Function.identity())),
b.collect(Collectors.toMap(o -> j.addAndGet(2), Function.identity())))
.flatMap(m -> m.entrySet().stream())
.sorted(Comparator.comparing(Map.Entry::getKey))
.forEach(e -> System.out.println(e.getValue())); // or collect
Output
one
two
three
four
five
six
@Holger's edit
Stream.concat(a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2), o)),
b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2), o)))
.sorted(Map.Entry.comparingByKey())
.forEach(e -> System.out.println(e.getValue())); // or collect
You don’t need to collect into maps, as you are only interested in getting the stream of entries, so you can simply useStream.concat( a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2),o)), b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2),o)) )
to get it. Then, you may chain.sorted(Map.Entry.comparingByKey())
. But you’re right in that this mutable state is discouraged. Most notably, it will have problems with parallel execution.
– Holger
Nov 15 '18 at 8:20
@Holger thank you, added this to the answer. I thought about it earlier, but couldn't find anEntrySet
constructor and got lazy to google search how to create an EntrySet :(
– Kartik
Nov 15 '18 at 23:21
It’s not creating anEntrySet
but just a stream ofEntry
instances. The ready-to-use implementations are indeed not easy to find (there’s also aSimpleImmutableEntry
inAbstractMap
). Starting with Java 9, you can simply useMap.entry(key, value)
to get an immutableEntry
instance, but you have to be aware that it does not supportnull
keys or values, so you can only use it when you can precludenull
.
– Holger
Nov 16 '18 at 7:39
add a comment |
One solution with Iterator
final Iterator<String> iterA = a.iterator();
final Iterator<String> iterB = b.iterator();
final Iterator<String> iter = new Iterator<String>() {
private final AtomicInteger idx = new AtomicInteger();
@Override
public boolean hasNext() {
return iterA.hasNext() || iterB.hasNext();
}
@Override
public String next() {
return idx.getAndIncrement() % 2 == 0 && iterA.hasNext() ? iterA.next() : iterB.next();
}
};
// Create target Stream with StreamEx from: https://github.com/amaembo/streamex
StreamEx.of(iter).forEach(System.out::println);
// Or Streams from Google Guava
Streams.stream(iter).forEach(System.out::println);
Or simply by the solution in abacus-util provided by me:
AtomicInteger idx = new AtomicInteger();
StreamEx.merge(a, b, (s1, s2) -> idx.getAndIncrement() % 2 == 0 ? Nth.FIRST : Nth.SECOND).forEach(Fn.println());
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53307682%2fhow-to-interleave-merge-two-java-8-streams%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
I’d use something like this:
public static <T> Stream<T> interleave(Stream<T> a, Stream<T> b) {
Spliterator<T> spA = a.spliterator(), spB = b.spliterator();
long s = spA.estimateSize() + spB.estimateSize();
if(s < 0) s = Long.MAX_VALUE;
int ch = spA.characteristics() & spB.characteristics()
& (Spliterator.NONNULL|Spliterator.SIZED);
ch |= Spliterator.ORDERED;
return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(s, ch) {
Spliterator<T> sp1 = spA, sp2 = spB;
@Override
public boolean tryAdvance(Consumer<? super T> action) {
Spliterator<T> sp = sp1;
if(sp.tryAdvance(action)) {
sp1 = sp2;
sp2 = sp;
return true;
}
return sp2.tryAdvance(action);
}
}, false);
}
It retains the characteristics of the input streams as far as possible, which allows certain optimizations (e.g. for count()
and toArray()
). Further, it adds the ORDERED
even when the input streams might be unordered, to reflect the interleaving.
When one stream has more elements than the other, the remaining elements will appear at the end.
add a comment |
I’d use something like this:
public static <T> Stream<T> interleave(Stream<T> a, Stream<T> b) {
Spliterator<T> spA = a.spliterator(), spB = b.spliterator();
long s = spA.estimateSize() + spB.estimateSize();
if(s < 0) s = Long.MAX_VALUE;
int ch = spA.characteristics() & spB.characteristics()
& (Spliterator.NONNULL|Spliterator.SIZED);
ch |= Spliterator.ORDERED;
return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(s, ch) {
Spliterator<T> sp1 = spA, sp2 = spB;
@Override
public boolean tryAdvance(Consumer<? super T> action) {
Spliterator<T> sp = sp1;
if(sp.tryAdvance(action)) {
sp1 = sp2;
sp2 = sp;
return true;
}
return sp2.tryAdvance(action);
}
}, false);
}
It retains the characteristics of the input streams as far as possible, which allows certain optimizations (e.g. for count()
and toArray()
). Further, it adds the ORDERED
even when the input streams might be unordered, to reflect the interleaving.
When one stream has more elements than the other, the remaining elements will appear at the end.
add a comment |
I’d use something like this:
public static <T> Stream<T> interleave(Stream<T> a, Stream<T> b) {
Spliterator<T> spA = a.spliterator(), spB = b.spliterator();
long s = spA.estimateSize() + spB.estimateSize();
if(s < 0) s = Long.MAX_VALUE;
int ch = spA.characteristics() & spB.characteristics()
& (Spliterator.NONNULL|Spliterator.SIZED);
ch |= Spliterator.ORDERED;
return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(s, ch) {
Spliterator<T> sp1 = spA, sp2 = spB;
@Override
public boolean tryAdvance(Consumer<? super T> action) {
Spliterator<T> sp = sp1;
if(sp.tryAdvance(action)) {
sp1 = sp2;
sp2 = sp;
return true;
}
return sp2.tryAdvance(action);
}
}, false);
}
It retains the characteristics of the input streams as far as possible, which allows certain optimizations (e.g. for count()
and toArray()
). Further, it adds the ORDERED
even when the input streams might be unordered, to reflect the interleaving.
When one stream has more elements than the other, the remaining elements will appear at the end.
I’d use something like this:
public static <T> Stream<T> interleave(Stream<T> a, Stream<T> b) {
Spliterator<T> spA = a.spliterator(), spB = b.spliterator();
long s = spA.estimateSize() + spB.estimateSize();
if(s < 0) s = Long.MAX_VALUE;
int ch = spA.characteristics() & spB.characteristics()
& (Spliterator.NONNULL|Spliterator.SIZED);
ch |= Spliterator.ORDERED;
return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(s, ch) {
Spliterator<T> sp1 = spA, sp2 = spB;
@Override
public boolean tryAdvance(Consumer<? super T> action) {
Spliterator<T> sp = sp1;
if(sp.tryAdvance(action)) {
sp1 = sp2;
sp2 = sp;
return true;
}
return sp2.tryAdvance(action);
}
}, false);
}
It retains the characteristics of the input streams as far as possible, which allows certain optimizations (e.g. for count()
and toArray()
). Further, it adds the ORDERED
even when the input streams might be unordered, to reflect the interleaving.
When one stream has more elements than the other, the remaining elements will appear at the end.
answered Nov 14 '18 at 21:05
HolgerHolger
164k23231439
164k23231439
add a comment |
add a comment |
A much dumber solution than Holger did, but may be it would fit your requirements:
private static <T> Stream<T> interleave(Stream<T> left, Stream<T> right) {
Spliterator<T> splLeft = left.spliterator();
Spliterator<T> splRight = right.spliterator();
T single = (T) new Object[1];
Stream.Builder<T> builder = Stream.builder();
while (splRight.tryAdvance(x -> single[0] = x) && splLeft.tryAdvance(builder)) {
builder.add(single[0]);
}
return builder.build();
}
This inconsistently includes all elements ofleft
, when it has more elements thanright
, but will drop elements ofright
when it has more thanleft
. You should decide. To included all common elements, usedo {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
, then decide. If you want to include all elements when the the streams have different sizes, do(splLeft.tryAdvance(builder)? splLeft: spRight).forEachRemaining(builder);
after the loop. And well,Stream.Builder<T>
does already implementConsumer<T>
, conveniently.
– Holger
Nov 15 '18 at 8:26
@Holger indeed i wanted only common ones, but now the problem is worse, sincedo {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
will still take an element fromleft
, so it's still not correct :(. the bigger problem I see now that I think about it - is that this is cheating,Stream.Builder
still uses a hidden collection to gather elements...
– Eugene
Nov 15 '18 at 9:16
Taking one additional element from left wouldn’t contradict the “interleaving” pattern (ababa). If you don’t want that, consider tracking the count and apply alimit
to the resulting stream. That’s simpler than dealing with an additional storage operation, especially with generic arrays. The builder implies a storage, I thought you were aware of it, as that’s the main (only) disadvantage over implementing a spliterator.
– Holger
Nov 15 '18 at 9:44
@Holger I thought about alimit
too, but don't know, the more I think about it, the more I like what you did
– Eugene
Nov 15 '18 at 9:46
1
@FedericoPeraltaSchaffner right, that is the exact point about the in memory collection, overall, just use whatever Holger put in place...
– Eugene
Nov 15 '18 at 14:44
|
show 5 more comments
A much dumber solution than Holger did, but may be it would fit your requirements:
private static <T> Stream<T> interleave(Stream<T> left, Stream<T> right) {
Spliterator<T> splLeft = left.spliterator();
Spliterator<T> splRight = right.spliterator();
T single = (T) new Object[1];
Stream.Builder<T> builder = Stream.builder();
while (splRight.tryAdvance(x -> single[0] = x) && splLeft.tryAdvance(builder)) {
builder.add(single[0]);
}
return builder.build();
}
This inconsistently includes all elements ofleft
, when it has more elements thanright
, but will drop elements ofright
when it has more thanleft
. You should decide. To included all common elements, usedo {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
, then decide. If you want to include all elements when the the streams have different sizes, do(splLeft.tryAdvance(builder)? splLeft: spRight).forEachRemaining(builder);
after the loop. And well,Stream.Builder<T>
does already implementConsumer<T>
, conveniently.
– Holger
Nov 15 '18 at 8:26
@Holger indeed i wanted only common ones, but now the problem is worse, sincedo {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
will still take an element fromleft
, so it's still not correct :(. the bigger problem I see now that I think about it - is that this is cheating,Stream.Builder
still uses a hidden collection to gather elements...
– Eugene
Nov 15 '18 at 9:16
Taking one additional element from left wouldn’t contradict the “interleaving” pattern (ababa). If you don’t want that, consider tracking the count and apply alimit
to the resulting stream. That’s simpler than dealing with an additional storage operation, especially with generic arrays. The builder implies a storage, I thought you were aware of it, as that’s the main (only) disadvantage over implementing a spliterator.
– Holger
Nov 15 '18 at 9:44
@Holger I thought about alimit
too, but don't know, the more I think about it, the more I like what you did
– Eugene
Nov 15 '18 at 9:46
1
@FedericoPeraltaSchaffner right, that is the exact point about the in memory collection, overall, just use whatever Holger put in place...
– Eugene
Nov 15 '18 at 14:44
|
show 5 more comments
A much dumber solution than Holger did, but may be it would fit your requirements:
private static <T> Stream<T> interleave(Stream<T> left, Stream<T> right) {
Spliterator<T> splLeft = left.spliterator();
Spliterator<T> splRight = right.spliterator();
T single = (T) new Object[1];
Stream.Builder<T> builder = Stream.builder();
while (splRight.tryAdvance(x -> single[0] = x) && splLeft.tryAdvance(builder)) {
builder.add(single[0]);
}
return builder.build();
}
A much dumber solution than Holger did, but may be it would fit your requirements:
private static <T> Stream<T> interleave(Stream<T> left, Stream<T> right) {
Spliterator<T> splLeft = left.spliterator();
Spliterator<T> splRight = right.spliterator();
T single = (T) new Object[1];
Stream.Builder<T> builder = Stream.builder();
while (splRight.tryAdvance(x -> single[0] = x) && splLeft.tryAdvance(builder)) {
builder.add(single[0]);
}
return builder.build();
}
edited Nov 15 '18 at 10:35
answered Nov 15 '18 at 8:19
EugeneEugene
69.4k999164
69.4k999164
This inconsistently includes all elements ofleft
, when it has more elements thanright
, but will drop elements ofright
when it has more thanleft
. You should decide. To included all common elements, usedo {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
, then decide. If you want to include all elements when the the streams have different sizes, do(splLeft.tryAdvance(builder)? splLeft: spRight).forEachRemaining(builder);
after the loop. And well,Stream.Builder<T>
does already implementConsumer<T>
, conveniently.
– Holger
Nov 15 '18 at 8:26
@Holger indeed i wanted only common ones, but now the problem is worse, sincedo {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
will still take an element fromleft
, so it's still not correct :(. the bigger problem I see now that I think about it - is that this is cheating,Stream.Builder
still uses a hidden collection to gather elements...
– Eugene
Nov 15 '18 at 9:16
Taking one additional element from left wouldn’t contradict the “interleaving” pattern (ababa). If you don’t want that, consider tracking the count and apply alimit
to the resulting stream. That’s simpler than dealing with an additional storage operation, especially with generic arrays. The builder implies a storage, I thought you were aware of it, as that’s the main (only) disadvantage over implementing a spliterator.
– Holger
Nov 15 '18 at 9:44
@Holger I thought about alimit
too, but don't know, the more I think about it, the more I like what you did
– Eugene
Nov 15 '18 at 9:46
1
@FedericoPeraltaSchaffner right, that is the exact point about the in memory collection, overall, just use whatever Holger put in place...
– Eugene
Nov 15 '18 at 14:44
|
show 5 more comments
This inconsistently includes all elements ofleft
, when it has more elements thanright
, but will drop elements ofright
when it has more thanleft
. You should decide. To included all common elements, usedo {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
, then decide. If you want to include all elements when the the streams have different sizes, do(splLeft.tryAdvance(builder)? splLeft: spRight).forEachRemaining(builder);
after the loop. And well,Stream.Builder<T>
does already implementConsumer<T>
, conveniently.
– Holger
Nov 15 '18 at 8:26
@Holger indeed i wanted only common ones, but now the problem is worse, sincedo {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
will still take an element fromleft
, so it's still not correct :(. the bigger problem I see now that I think about it - is that this is cheating,Stream.Builder
still uses a hidden collection to gather elements...
– Eugene
Nov 15 '18 at 9:16
Taking one additional element from left wouldn’t contradict the “interleaving” pattern (ababa). If you don’t want that, consider tracking the count and apply alimit
to the resulting stream. That’s simpler than dealing with an additional storage operation, especially with generic arrays. The builder implies a storage, I thought you were aware of it, as that’s the main (only) disadvantage over implementing a spliterator.
– Holger
Nov 15 '18 at 9:44
@Holger I thought about alimit
too, but don't know, the more I think about it, the more I like what you did
– Eugene
Nov 15 '18 at 9:46
1
@FedericoPeraltaSchaffner right, that is the exact point about the in memory collection, overall, just use whatever Holger put in place...
– Eugene
Nov 15 '18 at 14:44
This inconsistently includes all elements of
left
, when it has more elements than right
, but will drop elements of right
when it has more than left
. You should decide. To included all common elements, use do {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
, then decide. If you want to include all elements when the the streams have different sizes, do (splLeft.tryAdvance(builder)? splLeft: spRight).forEachRemaining(builder);
after the loop. And well, Stream.Builder<T>
does already implement Consumer<T>
, conveniently.– Holger
Nov 15 '18 at 8:26
This inconsistently includes all elements of
left
, when it has more elements than right
, but will drop elements of right
when it has more than left
. You should decide. To included all common elements, use do {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
, then decide. If you want to include all elements when the the streams have different sizes, do (splLeft.tryAdvance(builder)? splLeft: spRight).forEachRemaining(builder);
after the loop. And well, Stream.Builder<T>
does already implement Consumer<T>
, conveniently.– Holger
Nov 15 '18 at 8:26
@Holger indeed i wanted only common ones, but now the problem is worse, since
do {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
will still take an element from left
, so it's still not correct :(. the bigger problem I see now that I think about it - is that this is cheating, Stream.Builder
still uses a hidden collection to gather elements...– Eugene
Nov 15 '18 at 9:16
@Holger indeed i wanted only common ones, but now the problem is worse, since
do {} while(splLeft.tryAdvance(builder) && spRight.tryAdvance(builder));
will still take an element from left
, so it's still not correct :(. the bigger problem I see now that I think about it - is that this is cheating, Stream.Builder
still uses a hidden collection to gather elements...– Eugene
Nov 15 '18 at 9:16
Taking one additional element from left wouldn’t contradict the “interleaving” pattern (ababa). If you don’t want that, consider tracking the count and apply a
limit
to the resulting stream. That’s simpler than dealing with an additional storage operation, especially with generic arrays. The builder implies a storage, I thought you were aware of it, as that’s the main (only) disadvantage over implementing a spliterator.– Holger
Nov 15 '18 at 9:44
Taking one additional element from left wouldn’t contradict the “interleaving” pattern (ababa). If you don’t want that, consider tracking the count and apply a
limit
to the resulting stream. That’s simpler than dealing with an additional storage operation, especially with generic arrays. The builder implies a storage, I thought you were aware of it, as that’s the main (only) disadvantage over implementing a spliterator.– Holger
Nov 15 '18 at 9:44
@Holger I thought about a
limit
too, but don't know, the more I think about it, the more I like what you did– Eugene
Nov 15 '18 at 9:46
@Holger I thought about a
limit
too, but don't know, the more I think about it, the more I like what you did– Eugene
Nov 15 '18 at 9:46
1
1
@FedericoPeraltaSchaffner right, that is the exact point about the in memory collection, overall, just use whatever Holger put in place...
– Eugene
Nov 15 '18 at 14:44
@FedericoPeraltaSchaffner right, that is the exact point about the in memory collection, overall, just use whatever Holger put in place...
– Eugene
Nov 15 '18 at 14:44
|
show 5 more comments
As you can see from the question comments, I gave this a go using zip:
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
Stream<String> out = interleave(a, b);
public static <T> Stream<T> interleave(Stream<T> streamA, Stream<T> streamB) {
return zip(streamA, streamB, (o1, o2) -> Stream.of(o1, o2)).flatMap(s -> s);
}
/**
* https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip
**/
private static <A, B, C> Stream<C> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
final Iterator<A> iteratorA = streamA.iterator();
final Iterator<B> iteratorB = streamB.iterator();
final Iterator<C> iteratorC = new Iterator<C>() {
@Override
public boolean hasNext() {
return iteratorA.hasNext() && iteratorB.hasNext();
}
@Override
public C next() {
return zipper.apply(iteratorA.next(), iteratorB.next());
}
};
final boolean parallel = streamA.isParallel() || streamB.isParallel();
return iteratorToFiniteStream(iteratorC, parallel);
}
private static <T> Stream<T> iteratorToFiniteStream(Iterator<T> iterator, boolean parallel) {
final Iterable<T> iterable = () -> iterator;
return StreamSupport.stream(iterable.spliterator(), parallel);
}
add a comment |
As you can see from the question comments, I gave this a go using zip:
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
Stream<String> out = interleave(a, b);
public static <T> Stream<T> interleave(Stream<T> streamA, Stream<T> streamB) {
return zip(streamA, streamB, (o1, o2) -> Stream.of(o1, o2)).flatMap(s -> s);
}
/**
* https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip
**/
private static <A, B, C> Stream<C> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
final Iterator<A> iteratorA = streamA.iterator();
final Iterator<B> iteratorB = streamB.iterator();
final Iterator<C> iteratorC = new Iterator<C>() {
@Override
public boolean hasNext() {
return iteratorA.hasNext() && iteratorB.hasNext();
}
@Override
public C next() {
return zipper.apply(iteratorA.next(), iteratorB.next());
}
};
final boolean parallel = streamA.isParallel() || streamB.isParallel();
return iteratorToFiniteStream(iteratorC, parallel);
}
private static <T> Stream<T> iteratorToFiniteStream(Iterator<T> iterator, boolean parallel) {
final Iterable<T> iterable = () -> iterator;
return StreamSupport.stream(iterable.spliterator(), parallel);
}
add a comment |
As you can see from the question comments, I gave this a go using zip:
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
Stream<String> out = interleave(a, b);
public static <T> Stream<T> interleave(Stream<T> streamA, Stream<T> streamB) {
return zip(streamA, streamB, (o1, o2) -> Stream.of(o1, o2)).flatMap(s -> s);
}
/**
* https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip
**/
private static <A, B, C> Stream<C> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
final Iterator<A> iteratorA = streamA.iterator();
final Iterator<B> iteratorB = streamB.iterator();
final Iterator<C> iteratorC = new Iterator<C>() {
@Override
public boolean hasNext() {
return iteratorA.hasNext() && iteratorB.hasNext();
}
@Override
public C next() {
return zipper.apply(iteratorA.next(), iteratorB.next());
}
};
final boolean parallel = streamA.isParallel() || streamB.isParallel();
return iteratorToFiniteStream(iteratorC, parallel);
}
private static <T> Stream<T> iteratorToFiniteStream(Iterator<T> iterator, boolean parallel) {
final Iterable<T> iterable = () -> iterator;
return StreamSupport.stream(iterable.spliterator(), parallel);
}
As you can see from the question comments, I gave this a go using zip:
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
Stream<String> out = interleave(a, b);
public static <T> Stream<T> interleave(Stream<T> streamA, Stream<T> streamB) {
return zip(streamA, streamB, (o1, o2) -> Stream.of(o1, o2)).flatMap(s -> s);
}
/**
* https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip
**/
private static <A, B, C> Stream<C> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
final Iterator<A> iteratorA = streamA.iterator();
final Iterator<B> iteratorB = streamB.iterator();
final Iterator<C> iteratorC = new Iterator<C>() {
@Override
public boolean hasNext() {
return iteratorA.hasNext() && iteratorB.hasNext();
}
@Override
public C next() {
return zipper.apply(iteratorA.next(), iteratorB.next());
}
};
final boolean parallel = streamA.isParallel() || streamB.isParallel();
return iteratorToFiniteStream(iteratorC, parallel);
}
private static <T> Stream<T> iteratorToFiniteStream(Iterator<T> iterator, boolean parallel) {
final Iterable<T> iterable = () -> iterator;
return StreamSupport.stream(iterable.spliterator(), parallel);
}
answered Nov 15 '18 at 20:58
BlundellBlundell
56.9k27161193
56.9k27161193
add a comment |
add a comment |
This may not be a good answer because
(1) it collects to map, which you don't want to do I guess and
(2) it is not completely stateless as it uses AtomicIntegers.
Still adding it because
(1) it is readable and
(2) community can get an idea from this and try to improve it.
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
AtomicInteger i = new AtomicInteger(0);
AtomicInteger j = new AtomicInteger(1);
Stream.of(a.collect(Collectors.toMap(o -> i.addAndGet(2), Function.identity())),
b.collect(Collectors.toMap(o -> j.addAndGet(2), Function.identity())))
.flatMap(m -> m.entrySet().stream())
.sorted(Comparator.comparing(Map.Entry::getKey))
.forEach(e -> System.out.println(e.getValue())); // or collect
Output
one
two
three
four
five
six
@Holger's edit
Stream.concat(a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2), o)),
b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2), o)))
.sorted(Map.Entry.comparingByKey())
.forEach(e -> System.out.println(e.getValue())); // or collect
You don’t need to collect into maps, as you are only interested in getting the stream of entries, so you can simply useStream.concat( a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2),o)), b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2),o)) )
to get it. Then, you may chain.sorted(Map.Entry.comparingByKey())
. But you’re right in that this mutable state is discouraged. Most notably, it will have problems with parallel execution.
– Holger
Nov 15 '18 at 8:20
@Holger thank you, added this to the answer. I thought about it earlier, but couldn't find anEntrySet
constructor and got lazy to google search how to create an EntrySet :(
– Kartik
Nov 15 '18 at 23:21
It’s not creating anEntrySet
but just a stream ofEntry
instances. The ready-to-use implementations are indeed not easy to find (there’s also aSimpleImmutableEntry
inAbstractMap
). Starting with Java 9, you can simply useMap.entry(key, value)
to get an immutableEntry
instance, but you have to be aware that it does not supportnull
keys or values, so you can only use it when you can precludenull
.
– Holger
Nov 16 '18 at 7:39
add a comment |
This may not be a good answer because
(1) it collects to map, which you don't want to do I guess and
(2) it is not completely stateless as it uses AtomicIntegers.
Still adding it because
(1) it is readable and
(2) community can get an idea from this and try to improve it.
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
AtomicInteger i = new AtomicInteger(0);
AtomicInteger j = new AtomicInteger(1);
Stream.of(a.collect(Collectors.toMap(o -> i.addAndGet(2), Function.identity())),
b.collect(Collectors.toMap(o -> j.addAndGet(2), Function.identity())))
.flatMap(m -> m.entrySet().stream())
.sorted(Comparator.comparing(Map.Entry::getKey))
.forEach(e -> System.out.println(e.getValue())); // or collect
Output
one
two
three
four
five
six
@Holger's edit
Stream.concat(a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2), o)),
b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2), o)))
.sorted(Map.Entry.comparingByKey())
.forEach(e -> System.out.println(e.getValue())); // or collect
You don’t need to collect into maps, as you are only interested in getting the stream of entries, so you can simply useStream.concat( a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2),o)), b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2),o)) )
to get it. Then, you may chain.sorted(Map.Entry.comparingByKey())
. But you’re right in that this mutable state is discouraged. Most notably, it will have problems with parallel execution.
– Holger
Nov 15 '18 at 8:20
@Holger thank you, added this to the answer. I thought about it earlier, but couldn't find anEntrySet
constructor and got lazy to google search how to create an EntrySet :(
– Kartik
Nov 15 '18 at 23:21
It’s not creating anEntrySet
but just a stream ofEntry
instances. The ready-to-use implementations are indeed not easy to find (there’s also aSimpleImmutableEntry
inAbstractMap
). Starting with Java 9, you can simply useMap.entry(key, value)
to get an immutableEntry
instance, but you have to be aware that it does not supportnull
keys or values, so you can only use it when you can precludenull
.
– Holger
Nov 16 '18 at 7:39
add a comment |
This may not be a good answer because
(1) it collects to map, which you don't want to do I guess and
(2) it is not completely stateless as it uses AtomicIntegers.
Still adding it because
(1) it is readable and
(2) community can get an idea from this and try to improve it.
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
AtomicInteger i = new AtomicInteger(0);
AtomicInteger j = new AtomicInteger(1);
Stream.of(a.collect(Collectors.toMap(o -> i.addAndGet(2), Function.identity())),
b.collect(Collectors.toMap(o -> j.addAndGet(2), Function.identity())))
.flatMap(m -> m.entrySet().stream())
.sorted(Comparator.comparing(Map.Entry::getKey))
.forEach(e -> System.out.println(e.getValue())); // or collect
Output
one
two
three
four
five
six
@Holger's edit
Stream.concat(a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2), o)),
b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2), o)))
.sorted(Map.Entry.comparingByKey())
.forEach(e -> System.out.println(e.getValue())); // or collect
This may not be a good answer because
(1) it collects to map, which you don't want to do I guess and
(2) it is not completely stateless as it uses AtomicIntegers.
Still adding it because
(1) it is readable and
(2) community can get an idea from this and try to improve it.
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
AtomicInteger i = new AtomicInteger(0);
AtomicInteger j = new AtomicInteger(1);
Stream.of(a.collect(Collectors.toMap(o -> i.addAndGet(2), Function.identity())),
b.collect(Collectors.toMap(o -> j.addAndGet(2), Function.identity())))
.flatMap(m -> m.entrySet().stream())
.sorted(Comparator.comparing(Map.Entry::getKey))
.forEach(e -> System.out.println(e.getValue())); // or collect
Output
one
two
three
four
five
six
@Holger's edit
Stream.concat(a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2), o)),
b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2), o)))
.sorted(Map.Entry.comparingByKey())
.forEach(e -> System.out.println(e.getValue())); // or collect
edited Nov 15 '18 at 23:33
answered Nov 14 '18 at 23:26
KartikKartik
2,88931333
2,88931333
You don’t need to collect into maps, as you are only interested in getting the stream of entries, so you can simply useStream.concat( a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2),o)), b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2),o)) )
to get it. Then, you may chain.sorted(Map.Entry.comparingByKey())
. But you’re right in that this mutable state is discouraged. Most notably, it will have problems with parallel execution.
– Holger
Nov 15 '18 at 8:20
@Holger thank you, added this to the answer. I thought about it earlier, but couldn't find anEntrySet
constructor and got lazy to google search how to create an EntrySet :(
– Kartik
Nov 15 '18 at 23:21
It’s not creating anEntrySet
but just a stream ofEntry
instances. The ready-to-use implementations are indeed not easy to find (there’s also aSimpleImmutableEntry
inAbstractMap
). Starting with Java 9, you can simply useMap.entry(key, value)
to get an immutableEntry
instance, but you have to be aware that it does not supportnull
keys or values, so you can only use it when you can precludenull
.
– Holger
Nov 16 '18 at 7:39
add a comment |
You don’t need to collect into maps, as you are only interested in getting the stream of entries, so you can simply useStream.concat( a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2),o)), b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2),o)) )
to get it. Then, you may chain.sorted(Map.Entry.comparingByKey())
. But you’re right in that this mutable state is discouraged. Most notably, it will have problems with parallel execution.
– Holger
Nov 15 '18 at 8:20
@Holger thank you, added this to the answer. I thought about it earlier, but couldn't find anEntrySet
constructor and got lazy to google search how to create an EntrySet :(
– Kartik
Nov 15 '18 at 23:21
It’s not creating anEntrySet
but just a stream ofEntry
instances. The ready-to-use implementations are indeed not easy to find (there’s also aSimpleImmutableEntry
inAbstractMap
). Starting with Java 9, you can simply useMap.entry(key, value)
to get an immutableEntry
instance, but you have to be aware that it does not supportnull
keys or values, so you can only use it when you can precludenull
.
– Holger
Nov 16 '18 at 7:39
You don’t need to collect into maps, as you are only interested in getting the stream of entries, so you can simply use
Stream.concat( a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2),o)), b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2),o)) )
to get it. Then, you may chain .sorted(Map.Entry.comparingByKey())
. But you’re right in that this mutable state is discouraged. Most notably, it will have problems with parallel execution.– Holger
Nov 15 '18 at 8:20
You don’t need to collect into maps, as you are only interested in getting the stream of entries, so you can simply use
Stream.concat( a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2),o)), b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2),o)) )
to get it. Then, you may chain .sorted(Map.Entry.comparingByKey())
. But you’re right in that this mutable state is discouraged. Most notably, it will have problems with parallel execution.– Holger
Nov 15 '18 at 8:20
@Holger thank you, added this to the answer. I thought about it earlier, but couldn't find an
EntrySet
constructor and got lazy to google search how to create an EntrySet :(– Kartik
Nov 15 '18 at 23:21
@Holger thank you, added this to the answer. I thought about it earlier, but couldn't find an
EntrySet
constructor and got lazy to google search how to create an EntrySet :(– Kartik
Nov 15 '18 at 23:21
It’s not creating an
EntrySet
but just a stream of Entry
instances. The ready-to-use implementations are indeed not easy to find (there’s also a SimpleImmutableEntry
in AbstractMap
). Starting with Java 9, you can simply use Map.entry(key, value)
to get an immutable Entry
instance, but you have to be aware that it does not support null
keys or values, so you can only use it when you can preclude null
.– Holger
Nov 16 '18 at 7:39
It’s not creating an
EntrySet
but just a stream of Entry
instances. The ready-to-use implementations are indeed not easy to find (there’s also a SimpleImmutableEntry
in AbstractMap
). Starting with Java 9, you can simply use Map.entry(key, value)
to get an immutable Entry
instance, but you have to be aware that it does not support null
keys or values, so you can only use it when you can preclude null
.– Holger
Nov 16 '18 at 7:39
add a comment |
One solution with Iterator
final Iterator<String> iterA = a.iterator();
final Iterator<String> iterB = b.iterator();
final Iterator<String> iter = new Iterator<String>() {
private final AtomicInteger idx = new AtomicInteger();
@Override
public boolean hasNext() {
return iterA.hasNext() || iterB.hasNext();
}
@Override
public String next() {
return idx.getAndIncrement() % 2 == 0 && iterA.hasNext() ? iterA.next() : iterB.next();
}
};
// Create target Stream with StreamEx from: https://github.com/amaembo/streamex
StreamEx.of(iter).forEach(System.out::println);
// Or Streams from Google Guava
Streams.stream(iter).forEach(System.out::println);
Or simply by the solution in abacus-util provided by me:
AtomicInteger idx = new AtomicInteger();
StreamEx.merge(a, b, (s1, s2) -> idx.getAndIncrement() % 2 == 0 ? Nth.FIRST : Nth.SECOND).forEach(Fn.println());
add a comment |
One solution with Iterator
final Iterator<String> iterA = a.iterator();
final Iterator<String> iterB = b.iterator();
final Iterator<String> iter = new Iterator<String>() {
private final AtomicInteger idx = new AtomicInteger();
@Override
public boolean hasNext() {
return iterA.hasNext() || iterB.hasNext();
}
@Override
public String next() {
return idx.getAndIncrement() % 2 == 0 && iterA.hasNext() ? iterA.next() : iterB.next();
}
};
// Create target Stream with StreamEx from: https://github.com/amaembo/streamex
StreamEx.of(iter).forEach(System.out::println);
// Or Streams from Google Guava
Streams.stream(iter).forEach(System.out::println);
Or simply by the solution in abacus-util provided by me:
AtomicInteger idx = new AtomicInteger();
StreamEx.merge(a, b, (s1, s2) -> idx.getAndIncrement() % 2 == 0 ? Nth.FIRST : Nth.SECOND).forEach(Fn.println());
add a comment |
One solution with Iterator
final Iterator<String> iterA = a.iterator();
final Iterator<String> iterB = b.iterator();
final Iterator<String> iter = new Iterator<String>() {
private final AtomicInteger idx = new AtomicInteger();
@Override
public boolean hasNext() {
return iterA.hasNext() || iterB.hasNext();
}
@Override
public String next() {
return idx.getAndIncrement() % 2 == 0 && iterA.hasNext() ? iterA.next() : iterB.next();
}
};
// Create target Stream with StreamEx from: https://github.com/amaembo/streamex
StreamEx.of(iter).forEach(System.out::println);
// Or Streams from Google Guava
Streams.stream(iter).forEach(System.out::println);
Or simply by the solution in abacus-util provided by me:
AtomicInteger idx = new AtomicInteger();
StreamEx.merge(a, b, (s1, s2) -> idx.getAndIncrement() % 2 == 0 ? Nth.FIRST : Nth.SECOND).forEach(Fn.println());
One solution with Iterator
final Iterator<String> iterA = a.iterator();
final Iterator<String> iterB = b.iterator();
final Iterator<String> iter = new Iterator<String>() {
private final AtomicInteger idx = new AtomicInteger();
@Override
public boolean hasNext() {
return iterA.hasNext() || iterB.hasNext();
}
@Override
public String next() {
return idx.getAndIncrement() % 2 == 0 && iterA.hasNext() ? iterA.next() : iterB.next();
}
};
// Create target Stream with StreamEx from: https://github.com/amaembo/streamex
StreamEx.of(iter).forEach(System.out::println);
// Or Streams from Google Guava
Streams.stream(iter).forEach(System.out::println);
Or simply by the solution in abacus-util provided by me:
AtomicInteger idx = new AtomicInteger();
StreamEx.merge(a, b, (s1, s2) -> idx.getAndIncrement() % 2 == 0 ? Nth.FIRST : Nth.SECOND).forEach(Fn.println());
edited Nov 16 '18 at 18:52
answered Nov 16 '18 at 18:46
user_3380739user_3380739
90868
90868
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53307682%2fhow-to-interleave-merge-two-java-8-streams%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
zip is used to combine elements, I don't want to combine the elements, I want to keep the same total number of elements
– Blundell
Nov 14 '18 at 19:53
1
why wouldn't zip keep the same total number of elements?
– Aomine
Nov 14 '18 at 19:56
Reading the other thread, zip always takes a zipper function to combine an element from each stream to make a new element. I just want to interleave not zip
– Blundell
Nov 14 '18 at 19:56
2
For anyone coming here in the future, here is the comments + redirected answer : gist.github.com/blundell/3f062b8ec55fd1906c68e6ec8d848683
– Blundell
Nov 14 '18 at 20:13
1
I like the creation of the
interleave
method which essentially wraps thezip
method to improve readability et al. I've voted to reopen, so you could post that here instead of externally...– Aomine
Nov 14 '18 at 20:17