17

I have the following Java6 and Java8 code:

List<ObjectType1> lst1 = // a list of ObjectType1 objects
List<ObjectType2> lst2 = // a list of ObjectType1 objects, same size of lst1

List<ObjectType3> lst3 = new ArrayLis<ObjectType3>(lst1.size());
for(int i=0; i < lst1.size(); i++){
  lst3.add(new ObjectType3(lst1.get(i).getAVal(), lst2.get(i).getAnotherVal()));
}

Is there any way in Java8 to handle the previous for in a more concise way using Lambda?

6
  • 1
    Can you provide the actual constructor of ObjectType3? Commented Apr 8, 2014 at 9:46
  • 1
    Why use streams here? Your existing loop works fine and a stream will be less readable Commented Apr 8, 2014 at 9:50
  • @fge I know. I want a single line solution. About readability, in my opinion it is only a question of practice. I can understand on the fly the given solution. Thank you for pointing this out! Commented Apr 8, 2014 at 9:55
  • OK, but "single line" and "faster" are not the same... And it's very easy to do unreadable single liners (I know, I also do perl) :p Commented Apr 8, 2014 at 10:04
  • @fge I know, my question was looking for a more concise way, not faster! Commented Apr 8, 2014 at 10:10

3 Answers 3

29

A Stream is tied to a given iterable/Collection so you can't really "iterate" two collections in parallel.

One workaround would be to create a stream of indexes but then it does not necessarily improve over the for loop. The stream version could look like:

List<ObjectType3> lst3 = IntStream.range(0, lst1.size())
         .mapToObj(i -> new ObjectType3(lst1.get(i).getAVal(), lst2.get(i).getAnotherVal()))
         .collect(toList());
Sign up to request clarification or add additional context in comments.

4 Comments

@mat_boy it's not really shorter than what you had before. It sure is clever but I wouldnt consider it an improvement. Java8 doesnt deprecate a classic for loop :)
@JoeriHendrickx Sure, but I can assign the return value in a single line. I know, it is not important, but I prefer this approach.
@JoeriHendrickx Clearly if you write IntStream.range(0, lst1.size()).parallel() thus you may eventually speedup your execution time! This is a good point, isn't it? :)
euhm, sure. But it smells like premature optimization to me. Readability of your code should always be your first concern.
5

You could create a method that transforms two collections into a new collection, like this:

public <T, U, R> Collection<R> singleCollectionOf(final Collection<T> collectionA, final Collection<U> collectionB, final Supplier<Collection<R>> supplier, final BiFunction<T, U, R> mapper) {
    if (Objects.requireNonNull(collectionA).size() != Objects.requireNonNull(collectionB).size()) {
        throw new IllegalArgumentException();
    }
    Objects.requireNonNull(supplier);
    Objects.requireNonNull(mapper);
    Iterator<T> iteratorA = collectionA.iterator();
    Iterator<U> iteratorB = collectionB.iterator();
    Collection<R> returnCollection = supplier.get();
    while (iteratorA.hasNext() && iteratorB.hasNext()) {
        returnCollection.add(mapper.apply(iteratorA.next(), iteratorB.next()));
    }
    return returnCollection;
}

The important part here is that it will map the obtained iteratorA.next() and iteratorB.next() into a new object.

It is called like this:

List<Integer> list1 = IntStream.range(0, 10).boxed().collect(Collectors.toList());
List<Integer> list2 = IntStream.range(0, 10).map(n -> n * n + 1).boxed().collect(Collectors.toList());
singleCollectionOf(list1, list2, ArrayList::new, Pair::new).stream().forEach(System.out::println);

In your example it would be:

List<ObjectType3> lst3 = singleCollectionOf(lst1, lst2, ArrayList::new, ObjectType3::new);

Where for example Pair::new is a shorthand for the lamdda (t, u) -> new Pair(t, u).

Comments

0

I haven't found a way to update 1 stream to another, however, I accomplished a similar feat using a Map. :)

    Map<Integer, String> result = new HashMap<>();
    for(int index = 100; index > 0; index--){
        result.put(index, String.valueOf(index));
    }
    result.keySet().stream()
            .filter(key -> key%3 == 0)
            .sorted()
            .forEach(key -> result.put(key, "Fizz"));

    result.keySet().stream()
            .filter(key -> key%5 == 0)
            .sorted()
            .forEach(key -> result.put(key, "Buzz"));

    result.keySet().stream()
            .filter(key -> key%3 == 0 && key%5 == 0)
            .sorted()
            .forEach(key -> result.put(key, "FizzBuzz"));

    result.keySet().stream().forEach(key -> System.out.println(result.get(key)));

3 Comments

This answer is completely our of scope!
Seems to match the problem description: "Using Java8 Streams to create a list of objects from another" A map can be defined as 2 lists of Objects, with a 1 to 1 mapping from domain to range. Programming is all about creative solutions.
In my example I use List not Map. These are two different interfaces, As I said, I needed lists.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.