2

Suppose I have this bit of code:

Map<Consumer, Item> map = myStream.parallel()
        .filter(Objects::nonNull)
        .collect(Collectors.toConcurrentMap(e->e,
                e->e, mergeFunction));

What I want to do is call a method on each object of the stream after collecting is done.

For example,

item.setDate(item.getOneDate());

Before the code done sequentially looped through items, put into a map, and at the very end called a bit of the code like the one above, setting a 'date'.

while(iterator.hasNext()) {
   Item blah = iterator.next();
   ....
   // code for putting into map
   ...
   blah.setDate(blah.getOneDate());
}

Not sure how to do this with Java 8 streams. forEach? peek?

user1803551
  • 12,965
  • 5
  • 47
  • 74
SS'
  • 819
  • 1
  • 8
  • 18

3 Answers3

6

if this must be done after the collect operation, just use forEach:

map.forEach((k, v) -> {...});

if you're only interested in values:

map.values().forEach(item -> {...});

or only keys:

map.keySet().forEach(item -> {...});
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • Because the parallelization is there to speed things up, while this works, iterating through the map slows things down for me. Would be ideal if I could do it without having to loop through. – SS' Jul 24 '18 at 21:02
  • @SS' can you tell me why you have a merge function for the stream approach but don't have any logic to deal with duplicate keys on the imperative approach. – Ousmane D. Jul 24 '18 at 21:27
  • 1
    He didn't show his insertion code at all on the iterative approach. Either way, that collector looks like pseudocode, considering both key and value mappers are `e->e`. – shmosel Jul 24 '18 at 21:34
  • @shmosel agreed. I was just curious after I saw your answer. if the merge function is indeed needed this means we cannot simply do `.peek(item -> item.setDate(item.getOneDate()))` as that would even set the date of the objects that the merge function has discarded which may or may not be a problem. – Ousmane D. Jul 24 '18 at 21:38
  • Why would it be a problem? – shmosel Jul 24 '18 at 21:39
  • @shmosel because we're setting the date of objects that we don't even have in the result set. let's say the actual code was `Map map = myStream.parallel() .filter(Objects::nonNull) .collect(Collectors.toConcurrentMap(e-> getConsumer(e), e->e, (l,r) -> r));` calling `item.setDate` after the collect operation will yield a different result if were to do `.peek(item -> item.setDate(item.getOneDate()))` before the collect operation given there are actually key collisions. – Ousmane D. Jul 24 '18 at 21:44
  • I don't see why. Whichever element ends up in the map will only ever have its own date. – shmosel Jul 24 '18 at 21:47
  • @shmosel there might be a misunderstanding here. can you check this [ideone](https://ideone.com/wST1rO) and see what i mean. – Ousmane D. Jul 24 '18 at 22:02
  • I'm not sure what you're trying to demonstrate. I didn't dispute that there may be less items in the final result. – shmosel Jul 24 '18 at 22:21
2

This will be controversial, but I would use peek():

myStream.parallel()
        .filter(Objects::nonNull)
        .peek(item -> item.setDate(item.getOneDate()))

IMO, this use case invalidates many of the arguments against using peek() for state modification, since it's essentially side-effect free (assuming elements are no longer accessible from the stream source). It definitely complies with the only strict requirement of non-interference.

shmosel
  • 49,289
  • 6
  • 73
  • 138
0

I would agree with Jigar Joshi. A map operation is cleaner. And if you have no need for an intermediate object, just return the same one:

myStream.parallelStream()
        .map(i -> { i.setDate(i.getOneDate()); return i; })
        ...

That said, I would avoid mutating objects in the middle of a Stream. It's bad practice.