3

So I'm new to Java8. I've read about streams, but most examples are very basic. I was wondering how it'd be done with nested objects. Here is an example from my code:

for (Gas gas : gases) {
  resourceNodes.add(gas.getRefinery().unit());
}

It seems like I should be able to one-line this with a stream, but I can't quite figure it out. Could someone provide an answer with a stream. Is there a way to use :: syntax with nested methods too?

Edit: To clarify the example, getRefinery() returns an object of type: UnitInPool, whose method unit() returns an object of type: Unit. resourceNodes is an ArrayList of Unit.

0

5 Answers 5

3

The :: syntax that you refer to is what's known as a method reference.


Assuming resourceNodes is unassigned (or empty, in which case you can remove any previous assignment) prior to the for-loop, then you'd want to first map each Gas to whatever type unit() returns, and then collect the Stream to a List:

resourceNodes = gases.stream()
                     .map(Gas::getRefinery)
                     .map(GasRefinery::unit)
                     .collect(Collectors.toList());

Otherwise, if your goal is to simply add to resourceNodes, then it would be very similar:

resourceNodes.addAll(gases.stream()
                          .map(Gas::getRefinery)
                          .map(GasRefinery::unit)
                          .collect(Collectors.toList()));
Sign up to request clarification or add additional context in comments.

Comments

2

You need to provide more code for a reasonable answer, but I'm guessing you can get a stream of units, whetever these are (and whatever getRefinery returns) this way:

gases.stream().map(Gas::getRefinery).map(???::unit)

then you can for example collect the result with collect(Collectors.toList()) and just call resourceNodes.addAll with the collected result as parameter

Comments

1
resourceNodes = gases.stream().map(gas -> gas.getRefinery().unit()).collect(Collectors.toList());

Comments

1

If you want only method references, you can use this:

gases.stream().map(Gas::getRefinery).map(UnitInPool::unit).map(resourceNodes::add);
or
gases.stream().map(Gas::getRefinery).map(UnitInPool::unit).forEach(resourceNodes::add);

Otherwise, a lambda would likely be better since it's a lot shorter and more readable, and works, when you have methods that take multiple parameters or need to do multiple complex operations.

gases.stream().forEach(g -> resourceNodes.add(g.getRefinery().unit()));

This is basically the same as your previous code, but I would suggest the for-loop.

2 Comments

The last map will only work assuming resourceNodes::add is not a void returning method. Which is a reasonable assumption, as it probably is a java.util.List, but still. Even if it is a non-void method, mapping as means of just adding elements to an external collection smells hacky.
@pafauk. You're right, but other answers have assumed the same. Also, if I used a forEach there, it'd have been copying the answer Andy Turner originally posted. I'll change that though
1

Welcome to the SO community. I hope the following helps.

List<Unit> resourceNodes = gases.stream() // open a stream
.map(gas -> gas.getRefinery()) // convert to UnitInPool
.filter(unitInPool -> Objects.nonNull(unitInPool)) // null check to avoid NPE
.map(unip -> unip.getUnit()) // convert to Unit
.collect(Collectors.toList()) // collect all the values in a List

Comments

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.