2017-01-14 8 views
1

У меня есть POJO в Person.java файл с:У java8 метода фильтра Stream() и map() используются итерации?

public class Person { 
    private String name; 
    private int age; 

    public Person(String n, int a) { 
     name = n; 
     age = a; 
    } 

    public String getName() { 
     return name; 
    } 

    public int getAge() { 
     return age; 
    } 

    public boolean isAdult() { 
     return getAge() >= 18;  
    } 
} 

А потом у меня есть Demo.java файл, который создает список лиц и использует потоки для фильтрации и печати содержимого из списка:

import java.util.*; 

public class Demo { 
    public static void main(String[] args) { 
     List<Person> people = createPeople(); 
     List<String> names = people.stream() 
            .filter(person -> person.isAdult()) 
            .map(person -> person.getName()) 
            .collect(toList()); 
     System.out.println(names); 
    } 

    private static List<Person> createPeople() { 
     List<Person> people = new ArrayList<>(); 
     people.add("John", 19); 
     people.add("Joe", 21); 
     people.add("Jill", 16); 
     people.add("Sylvester", 18); 
     people.add("Hillary", 17); 
     people.add("Donald", 4); 

     return people; 
    } 
} 

I хотел знать:

1> Делает filter() и map() внутренне использует цикл для повторения всех Person ob в Listpeople?

2> Если да, то они пересекают все объекты в списке два разных раза (1-я итерация по filter() и другая по map())?

3> Если да, если я добавлю еще один метод map() или filter(), будет ли он снова пересекать все объекты в третий раз?

4> Если да, то как это отличается от нашей традиционной кодировки императивного стиля (по сути, в традиционном императивном стиле мы могли бы делать всю фильтрацию и отображение в течение одного единственного цикла в большинстве случаев. Таким образом, производительность в мудрый, императивный стиль кодирования будет работать лучше, чем потоки в таком случае.)?

PS: Если у вас есть No по любому из вышеуказанных вопросов, пожалуйста, добавьте объяснение относительно того, как это работает.

Еще одна: Есть ли разница в итерации, выполняемой потоком внутри, и итерация, которую мы делаем в императивном стиле? Прошу прощения еще немного, насколько я знаю, с подробным объяснением.

+1

1> да, но она перебирает поток, а не список. 2> нет 3> - 4> - –

+0

2> Нет? Итак, как это работает. @FrankPuffer –

+0

@FrankPuffer Как отличается итерация по потоку, итерация по списку? –

ответ

6

Во-первых, ваш код не компилируется, потому что map() возвращает Stream, а не List. Вы должны завершить цепочку потоков с помощью операции терминала, и оба filter() и map(): intermediate operations. Говорит так прямо в джавадоке. В вашем случае вам нужно добавить .collect(Collectors.toList()), чтобы он скомпилировался и работал нормально.

1> Есть ли filter() и map() внутренне использовать цикл для перебора всех Person объектов в List людей?

No. Терминальная операция выполняет цикл.

Поскольку вопросы с 2 по 4 принимают ответ Yes, у них нет ответа.

Если у вас есть No по любому из вышеуказанных вопросов, пожалуйста, добавьте объяснение относительно того, как все работает тогда.

Прочтите documentation или выполните поиск в Интернете. Это довольно хорошо объяснено.

Есть ли разница в итерации, выполняемой потоком внутри, и итерация, которую мы делаем в императивном стиле?

Да, например. потоки могут использовать параллельное выполнение потока. Даже однопоточность отличается тем, что вся операция работает, хотя и логически, на высоком уровне, они по существу одинаковы.


Пример

В коде, с добавил collect() вызов, эквивалентный императив код будет:

List<String> names = new ArrayList<>(); 
for (Person person : people) 
    if (person.isAdult()) 
     names.add(person.getName()); 

Для сравнения с тем, что делает логический поток, сначала определить лямбда перешли к filter() и map():

Predicate<Person>  filter = person -> person.isAdult(); 
Function<Person, String> map = person -> person.getName(); 

Тогда вы получите Collector по телефону Collectors.toList() и извлечения объектов он обеспечивает:

Collector<String, List<String>, List<String>> collector = (Collector) Collectors.toList(); 
Supplier<List<String>>    supplier = collector.supplier(); 
BiConsumer<List<String>, String>  accumulator = collector.accumulator(); 
Function<List<String>, List<String>> finisher = collector.finisher(); 

Теперь stream() вызова в основном обеспечивает Iterator (это на самом деле Spliterator) и collect вызов будет перебирать, так в сочетании они эквивалентны петле for. Я не буду описывать полную логику работы Stream, Spliterator и collect(). Найдите в Интернете, если вам нужно более подробно об этом.

Таким образом, необходимо for цикл выше становится:

List<String> list = supplier.get();  // list = new ArrayList<>() 
for (Person person : people)    // collect() loop using Spliterator from stream() 
    if (filter.test(person)) {    // if (person.isAdult()) 
     String value = map.apply(person); // value = person.getName() 
     accumulator.accept(list, value); // list.add(value) 
    } 
List<String> names = finisher.apply(list); // names = list 
+0

Довольно хорошо объяснил. Большое спасибо. Я добавил вызов 'collect()', чтобы избежать путаницы с другими читателями. –