使用地图中的值过滤pojos流

时间:2016-06-04 13:31:42

标签: java lambda java-8 java-stream

我有这个pojo(这是案例的一个例子,而不是真实的例子):

Book {
  String name;
  Map<String, String> properties;
}

以及它们的清单:

List<Book> library;

TheBook1, {["lang", "pt-br"],["type","romance"],["author","John"]}
TheBook2, {["lang", "en-US"],["type","fiction"],["author","Brian"],["translation","pt-br,es"}

假设我有一个Library集合,并且我有一个包含搜索条件的地图,例如:

Map<String, String> criteria = new HashMap<>();
criteria.put(BOOK_TYPE, "romance");
criteria.put(BOOK_LANG, "pt-br");

如何为库流编写过滤谓词,以便搜索符合所提供条件的所有书籍?匹配必须是现在的键和值的精确字符串匹配。

这样的事情:

Set<Book> result = library.stream()
.filter(b-> b.getProperties() **????**)
.collect(Collectors.toSet());

4 个答案:

答案 0 :(得分:5)

你可以简单地拥有:

List<Book> books = 
    library.stream()
           .filter(b -> b.properties.entrySet().containsAll(criteria.entrySet()))
           .collect(Collectors.toList());

这将过滤书籍,只保留那些属性包含所有给定条件的书籍。检查是使用containsAll完成的,这意味着它将保留包含完全给定条件的属性(通过条目相等匹配,因此键和值都相等)。

答案 1 :(得分:1)

library我猜是List<Book>吗?

考虑一下如何在不使用Java8 / Streams的情况下编写谓词。然后将谓词放在filter方法中。

即:

Book b = ...;
boolean matches = false;
if(b.properties != null) {
  for(Entry<String,String> e : b.properties) {
    if(e.getKey().equals("foo") && e.getValue().length > 3) {
      matches = true;
      break;
    }
  }
}

(我知道这个过滤器可以更简单地编写,如果需要可以优化它)

现在创建一个方法:

public static boolean myPredicate(Book b) {
  if(b.properties != null) {
    for(Entry<String,String> e : b.properties) {
      if(e.getKey().equals("foo") && e.getValue().length > 3) {
        return true;
      }
    }
  }
  return false;
}

然后将其放在您的信息流中:

Set<Book> result = library.stream()
  .filter(MyClass::myPredicate)
  .collect(Collectors.toSet());

您也可以将其直接放在filter方法中:

Set<Book> result = library.stream()
  .filter(b -> {
    if(b.properties != null) {
      for(Entry<String,String> e : b.properties) {
        if(e.getKey().equals("foo") && e.getValue().length > 3) {
          return true;
        }
      }
    }
    return false;
  })
  .collect(Collectors.toSet());
顺便说一下:你不应该需要toSet收藏家。只需创建List即可。应该更有效率。

答案 2 :(得分:1)

您可以使用以下语法:

Set<Book> filteredBooks = library
    .stream()
    .filter((a) -> {
                boolean answer;
                for(Entry<Integer, String> filter : filters.entrySet())
                    if (!isTrue(a, filter.getValue().getProperty())) return false;
                return true;
                   })
        .collect(Collectors.toCollection(HashSet::new));

虽然filters实际上是您拥有的Map条件。 &#34; get(1)&#34;是您的第一个标准等。我使用isTrue作为方法,为通过条件的图书返回boolean。如您所见,您可以使用多个条件。没问题。

祝你好运!

答案 3 :(得分:1)

that所述,lambda表达式的主体可以是单个表达式,也可以是语句块。所以你可以像这样编写你的谓词:(t) - &gt; {//声明1; // statement2; //返回结果;}。

Set<Book> result = library.stream()
            .filter( b -> {
    for (Map.Entry<String, String> entry : criteria.entrySet()) {
        String key = entry.getKey();
        if (!(b.getProperties().get(key) != null && b.getProperties().get(key).equals(entry.getValue()))) return false;
    }
    return true;
})
.collect(Collectors.toSet());

否则,您可以使用其谓词格式替换您的条件:

Set<Book> result3 = library.stream()
            .filter( b -> b.getProperties().get("BOOK_TYPE").equals("romance") && b.getProperties().get("BOOK_LANG").equals("Pt-Br"))
            .collect(Collectors.toSet());

,或者

Predicate<Book> bookType = b -> b.getProperties() != null &&  b.getProperties().get("BOOK_TYPE") != null && b.getProperties().get("BOOK_TYPE").equals("romance");
Predicate<Book> bookLang = b -> b.getProperties() != null && b.getProperties().get("BOOK_LANG") != null && b.getProperties().get("BOOK_LANG").equals("Pt-Br");
Set<Book> result3 = library.stream()
            .filter(bookType)
            .filter(bookLang)
            .collect(Collectors.toSet());