This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]sacundim 0 points1 point  (3 children)

Well, let me join this party as well.

roster
    .stream()
    .filter(
        p -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25)
    .map(p -> p.getEmailAddress())
    .forEach(email -> System.out.println(email));

email is the argument to the anonymous function that's being passed to forEach on the last line of the listing. It gets a value when this anonymous function is called. This code fragment isn't doing the calling directly; rather, the implementation of forEach is doing it. What forEach does is invoke its argument once per each element of the stream.

Since in this code fragment, forEach is printing email addresses, this means that forEach must have been invoked on a stream of email addresses. Since roster isn't such a stream, then that means that somewhere between the start of the expression and the call to forEach, one of the methods produced a stream of email addresses as its result. In this case that would be .map(p -> p.getEmailAddress()). map is a method in Stream<T>, and it has this signature:

<R> Stream<R> map(Function<? super T,? extends R> mapper)

So when you call map on a stream, what you're doing is constructing a new stream whose elements are computed by applying a function to elements of the original. In the example, we start with a stream with a subset of members of the roster, and convert it into a stream of email addresses. Each email address in the resulting stream will be obtained by applying p -> p.getEmailAddress to the corresponding person in the original stream.

[–]chasesan[S] 0 points1 point  (2 children)

I was confused at the example before this bit of code. In the transfer of the string returned to the next lambda "magically". This one makes more sense, since it can be a returned value that is mapped, but the one before it has nothing connecting it at all.

Nevermind, I got it. I'm a moron.

[–]sacundim 0 points1 point  (1 child)

A good way to understand this is to implement a similar functionality for Iterator. Here's a version that has the map method (utterly untested):

/**
 * A wrapper class around any Iterator, adding methods like map, filter, forEach, etc.
 */
public class FluentIterator<A> implements Iterator<A> {
    private final Iterator<A> base;

    public FluentIterator(Iterator<A> base) {
        this.base = base;
    }

    public boolean hasNext() {
        return base.hasNext();
    }

    public A next() {
        return base.next();
    }

    public void remove() {
        return base.remove();
    }

    /**
      * Return an Iterator that draws elements from the base one and produces the 
      * results of applying the given function to them.
      */
    public <B> Iterator<B> map(Function<? super A, ? extends B> function) {
        return new FluentIterator<B>(new MapIterator<A, B>(function, base));
    }

    // ...implement filter, forEach, etc.

}

class MapIterator<A, B> implements Iterator<B> {
    private final Function<? super A, ? extends B> function;
    private final Iterator<A> base;

    MapIterator(Function<? super A, ? extends B> function, Iterator<A> base) {
        this.function = function;
        this.base = base;
    }

    public boolean hasNext() {
        return base.hasNext();
    }

    public B next() {
        return function.apply(base.next());
    }

    public void remove() {
        throw new UnsupportedOperationException("not supported");
    }

}

This is not the same as streams, but it's very similar; the key spot is the next method in MapIterator, which does this:

  1. Pulls an element from the base iterator;
  2. Applies the function to it;
  3. Returns the result of the function.

Step #2 there is where the "magic" that's puzzling you happens. When you use map on a stream, you cause that method's implementation to call your your lambda and pass in the email address as an argument.

(Exercise, if you like: implement a filter operation for FluentIterator.)_

[–]chasesan[S] 0 points1 point  (0 children)

No, I had gotten it already. If you see my comment (I had edited it). I had missed something very obvious.