2017-02-03 53 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简单更换收集器。