2016-03-16 2 views
2

У меня есть 2 списка. 1 список идентификаторов и другой список полон Foo объектов, назовут его список А. Foo класса выглядит следующим образом:Java Stream multi list iteration

public class Foo { 
    private String id; 
    /* other member variables */ 

    Foo(String id) { 
     this.id = id; 
    } 

    public String getId() { 
     return id; 
    } 
} 

У меня есть простой список идентификаторов, как List<Integer>, вызови его список B. Что Я хочу сделать, это перебрать список B, один элемент, в то время, возьмите идентификатор, сравнить его в список а и захватить Foo объект с эквивалентным идентификатором, а затем добавить Foo объект в новом списке, списке C.

Я пытаюсь конкатенировать потоки, но я новичок в потоках, и я завязываюсь всеми методами, такими как map, filter, forEach. Я не уверен, что использовать когда.

+0

Вы можете поместить все объекты Foo в карту с идентификатором в качестве ключа, а затем получить объект Foo по ID – BobTheBuilder

ответ

1

Прямой путем будет то, что вы имеете в своем посте: цикл по идентификаторам, выберите первый Foo, имеющий этот идентификатор, и если один, если найдено, собрать его в List. Помещенный в код, он будет выглядеть следующим образом: каждый идентификатор сопоставляется с соответствующим Foo, который найден путем вызова findFirst() на foos с этим id. Это возвращает Optional, которые отфильтрованы, Foo не существует.

List<Integer> ids = Arrays.asList(1, 2, 3); 
List<Foo> foos = Arrays.asList(new Foo("2"), new Foo("1"), new Foo("4")); 

List<Foo> result = 
    ids.stream() 
     .map(id -> foos.stream().filter(foo -> foo.getId().equals(id.toString())).findFirst()) 
     .filter(Optional::isPresent) 
     .map(Optional::get) 
     .collect(Collectors.toList()); 

Большая проблема с этим подходом является то, что вам нужно пересечь foos лист столько раз, сколько есть идентификатор, чтобы посмотреть. Лучшим решением было бы первым бы создать просмотровых Map, где каждый идентификатор карты к Foo:

Map<Integer, Foo> map = foos.stream().collect(Collectors.toMap(f -> Integer.valueOf(f.getId()), f -> f)); 

List<Foo> result = ids.stream().map(map::get).filter(Objects::nonNull).collect(Collectors.toList()); 

В этом случае, мы смотрим вверх на Foo и отфильтровать null элементы, которые ни в коей мере Foo не было найдено.


Другим совершенно другой подход не пересекать идентификаторы и поиска Foo, но фильтровать Foo S, имеющим идентификатор, который содержится в розыске идентификаторов. Проблема с подходом заключается в том, что для этого требуется сортировать выходной список, чтобы порядок получаемого списка соответствовал порядку идентификаторов.

+0

. Это то, чего я хочу. Я понял, что не полностью представляю свой сценарий в своем оригинальном посте. Мой фактический сценарий имеет 2 списка объектов Foo, но в первом списке есть только объекты Foo с идентификаторами, а у другого есть полные объекты Foo со значениями для других переменных-членов. Я хотел сравнить их и получить полные объекты foo. Ваше первое решение отлично работало! Но я не мог заставить второй работать в моем сценарии – Richard

+1

@Richard Во втором фрагменте, если у вас нет списка id, а список частичного 'Foo', вам сначала нужно сопоставить каждый' Foo' к его id, что-то вроде 'myPartialFoos.stream(). map (Foo :: getId) .map (map :: get) ...' – Tunaki

+0

совершенный снова. Спасибо за весь вход! – Richard

0

Я бы реализовать это следующим образом:

List<Foo> list = Arrays.asList(
    new Foo("abc"), 
    new Foo("def"), 
    new Foo("ghi") 
); 

List<String> ids = Arrays.asList("abc", "def", "xyz"); 

//Index Foo by ids 
Map<String, Foo> map = list.stream() 
    .collect(Collectors.toMap(Foo::getId, Function.identity())); 

//Iterate on ids, find the corresponding elements in the map 
List<Foo> result = ids.stream().map(map::get) 
    .filter(Objects::nonNull) //Optional... 
    .collect(Collectors.toList());