2017-02-03 55 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); 


    } 

} 

我尋找Java 8層的功能可能流的globalBooks響應和根據BooksContent中的說明字段收集書籍列表的地圖。

Map<String,List<Books>>即所有具有相同描述的書籍應該按描述分組在一起嗎?

我可以通過常規的java做到這一點,但代碼變得太凌亂和不整潔。

+0

由於每本書都有多個不同描述的內容,您需要每本書出現在多個地圖條目中,是否正確? –

+0

類似的問題,使用番石榴'MultiMap':[最清潔的方式來索引一個屬性的項目屬性,它本身是一個集合](http://stackoverflow.com/questions/23003754/cleanest-way-to-index-a - 收集該物品本身是-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

我認爲這個答案很好,因爲它表明使用computeIfAbsent的標準Java循環寫起來更短,更容易閱讀,並且(可能)更快地執行。但是,您不應該使用'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簡單更換收集器。