3

Using a Java 8 lambda expression, I'm trying to do something like this.

List<NewObject> objs = ...;
for (OldObject oldObj : oldObjects) {
  NewObject obj = oldObj.toNewObject();
  obj.setOrange(true);
  objs.add(obj);
}

I wrote this code.

oldObjects.stream()
  .map(old -> old.toNewObject())
  .forEach({new.setOrange("true")})
  .collect(Collectors.toList()); 

This is invalid code because I'm then trying to do .collect() on what's returned by .forEach(), but forEach is void and does not return a list.

How should this be structured?

Remi Guan
  • 21,506
  • 17
  • 64
  • 87
Jeremy
  • 5,365
  • 14
  • 51
  • 80

2 Answers2

6

You can use Stream's peek method, which returns the Stream because it's an intermediate operation. It normally isn't supposed to have a side effect (it's supposed to be "non-interfering"), but in this case, I think the side effect (setOrange(true)) is intended and is fine.

List<NewObject> newObjects =
    oldObjects.stream()
        .map(OldObject::toNewObject)
        .peek( n -> n.setOrange(true))
        .collect(Collectors.toList());

It's about as verbose as your non-streams code, so you can choose which technique to use.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • 1
    There's a difference between side-effect-free and non-interfering. (Note that a void-returning lambda can *only* have side-effects, except for the trivial `e -> {}` consumer.) Determining that the lambda passed to `peek` actually is non-interfering is not always trivial (though what you've got here *looks* harmless enough.) – Brian Goetz Nov 06 '15 at 00:41
  • it's not more interfering than `peek( n -> print(n) )` – ZhongYu Nov 06 '15 at 02:40
  • 1
    @bayou.io: this is not correct. Mutating a property of a stream element can be interfering, e.g. if the same element appears multiple times within the same stream. This is different to a `print` operation. But since `oldObj.toNewObject()` seems to create new instances we can conclude that this is not the case here. However, it still would be cleaner to construct the `NewObject` instance and set the `orange` property in one lambda within the `map` operation. Then, `peek` would be unnecessary. – Holger Nov 06 '15 at 12:00
5

You can use peek.

List<NewObject> list = oldObjects.stream()
                                 .map(OldObject::toNewObject)
                                 .peek(o -> o.setOrange(true))
                                 .collect(Collectors.toList());

Alternatively, you can mutate the elements after forming the list.

List<NewObject> list = oldObjects.stream()
                                 .map(OldObject::toNewObject)
                                 .collect(Collectors.toList());
list.forEach(o -> o.setOrange(true));
Paul Boddington
  • 37,127
  • 10
  • 65
  • 116