2017-02-03 7 views
2

У меня есть список (GlobalBooks), который выглядит примерно нижеJava 8: Перебор и группы объектов из списка на основе атрибутов объекта

import java.util.ArrayList; 
import java.util.List; 

public class Main { 

    public static void main(String[] args) { 

     GlobalBooks globalBooks = new GlobalBooks(); 

     List<Book> bookList = new ArrayList<Book>(); 


     Book book = new Book(); 
     List<BookContent> bookContents = new ArrayList<BookContent>(); 

     book.setBookName("A"); 
     BookContent content = new BookContent(); 
     content.setDescription("December 2016"); 
     content.setComponentID(20l); 
     bookContents.add(content); 

     content = new BookContent(); 
     content.setDescription("January 2016"); 
     content.setComponentID(30l); 
     bookContents.add(content); 

     content = new BookContent(); 
     content.setDescription("Febuary 2016"); 
     content.setComponentID(40l); 
     bookContents.add(content); 
     book.setContents(bookContents); 

     bookList.add(book); 


     book = new Book(); 
     bookContents = new ArrayList<BookContent>(); 

     book.setBookName("B"); 
     content = new BookContent(); 
     content.setDescription("December 2016"); 
     content.setComponentID(20l); 
     bookContents.add(content); 

     content = new BookContent(); 
     content.setDescription("January 2016"); 
     content.setComponentID(30l); 
     bookContents.add(content); 

     content = new BookContent(); 
     content.setDescription("Febuary 2016"); 
     content.setComponentID(40l); 
     bookContents.add(content); 
     book.setContents(bookContents); 

     bookList.add(book); 

     globalBooks.setBooks(bookList); 


     System.out.println(globalBooks); 


    } 

} 

Ищу ява 8 функций, которые могут течь ответ globalBooks и собрать карту Список книг на основе поля описания в BooksContent.

Map<String,List<Books>> i.e все книги с тем же описанием должны быть сгруппированы вместе описанием?

Я могу сделать это с помощью обычной java, но код становится слишком грязным и неопрятным.

+0

Поскольку каждая книга содержит несколько материалов с различными описаниями, вам нужно, чтобы каждая книга отображалась в нескольких записях карты, правильно? –

+0

Аналогичный вопрос с использованием Guava 'MultiMap': [Самый чистый способ индексирования коллекции по свойству элемента, который сам является коллекцией) (http://stackoverflow.com/questions/23003754/cleanest-way-to-index-a -collection-by-a-the-items-that-yourself-is-a) – MikaelF

ответ

6

Поскольку каждая книга содержит несколько материалов с различными описаниями, вам потребуется, чтобы каждая книга отображалась в нескольких записях карты. Поэтому я бы использовал flatMap для создания элемента потока для каждого BookContent, в частности пары Description + Book. Затем соберите в карту, как обычно, с Collectors.groupingBy:

Map<String,List<Book>> result = globalBooks.getBooks().stream().flatMap(
     (Book b) -> b.getContents().stream().map(
      (BookContent bc) -> new Pair<>(bc.getDescription(), b) 
     ) 
    ).collect(Collectors.groupingBy(
     Pair::getKey, 
     Collectors.mapping(Pair::getValue, Collectors.toList()) 
    )); 

Примечание:

Класс пара я Использованный выше от javafx.util.Pair. Однако вы можете так же легко использовать AbstractMap.SimpleEntry, свой собственный класс Pair или любой тип данных коллекции, способный удерживать два объекта.

1

Другим решением может быть

Map<String,List<Book>> result = new HashMap<String, List<Book>>(); 

    globalBooks.getBooks().stream().forEach(book->{ 

     book.getContents().stream().forEach(bookContent->{ 

      result.computeIfAbsent(bookContent.getDescription(),(list)->new ArrayList<Book>()).add(book); 
     }); 
    }); 
+2

Этот код без необходимости использует побочные эффекты. Если он выполняется параллельно, то безопасность, не связанная с потоком HashMap, приведет к неправильным результатам, и добавление необходимой синхронизации вызовет конфликт. –

+1

Я думаю, что этот ответ хорош, потому что он показывает, что стандартный цикл Java, использующий computeIfAbsent, короче, чтобы читать, читать легче и (возможно) быстрее выполнять. Однако вы не должны использовать 'Stream.forEach', просто стандартный цикл' for (item: collection) ', как описано в моем предыдущем комментарии. –

1

Это сделает работу:

Map<String, List<String>> result = bookList.stream().flatMap(b -> b.getContents().stream()) 
    .collect(Collectors.toMap(str -> str, str -> bookList.stream() 
    .filter(b -> b.getContents().contains(str)) 
    .collect(Collectors.toList()), (a, b) -> {a.addAll(b);return a;})); 

Если вы хотите, чтобы выполнить это параллельно, просто заменить коллектор с Collectors.toConcurrentMap.

 Смежные вопросы

  • Нет связанных вопросов^_^