首页 > 解决方案 > 使用 Java 8 Streams 完成删除循环

问题描述

我有一个需要根据特定标准映射的文档对象列表。有一个实用程序函数可以接受任何 2 种文档类型,并确定它们是否符合许多标准,例如文档类型、它们是否共享任何作者等。代码有效,但我想使用 Java Streams 来解决它如果可能的话。

我目前使用以下代码解决了这个问题:

  class Document{
     private String genre;
     private List<Author> authors;
     private String isbn;
     private boolean paperBack;
     ...
  }

我还使用了一个库实用程序,该实用程序具有在给定一系列匹配条件和一对文档的情况下返回 true 的函数。它只是返回一个布尔值。

   boolean matchesOnCriteria(Document doc1, Document doc2, Criteria criteria){
       ...
   }

这是用于查找与提供的条件匹配的书籍的匹配代码

     DocumentUtils utils = DocumentUitls.instance();
     Criteria userCriteria = ...
     List<Pair> matches = new ArrayList<>();

    List<Document> documents = entityManager.findBy(.....);

   for(Document doc1 : documents){
      for(Documents doc2 : documents){
         if(!doc1.equals(doc2){
             if (utils.matchesOnCriteria(doc1,doc2, userCriteria)) {
              Pair<Document> p = new Pair(doc1,doc2);
               if(!matches.contains(p)){
                 matches.add(p);
               }
             }
           }
          } 
        }  
     }

如何使用 Streams 做到这一点?

标签: javalambdajava-8java-stream

解决方案


使用以下解决方案的想法Steam::reduce很简单:

  1. 将合格的文档对分组为Map<Document, List<Document>>具有所有可能的可接受组合。假设奇数和偶数文档是成对的:

    D1=[D3, D5], D2=[D4], D3=[D1, D5], D4=[D2], D5[D1, D3]  // dont mind the duplicates 
    
  2. 使用Stream::reduce您可以实现以下步骤:

    • 将条目转换为Pair<>,

      D1-D3, D1-D5, D2-D4, D3-D1, D1-D5, D4-D2, D5-D1, D5-D3
      
    • 保存这些项目以Set保证相等的对出现一次 ( D1-D3= D3-D1)。条件 the Pairmust override both Object::equalsand and Object:hashCodeand 实现基于存在的两个文档的相等性。

      D1-D3, D1-D5, D3-D5, D2-D4
      
    • 将特定集合减少(合并)为单个集合Set<Pair<Document>>

Map<Document, List<Document>> map = documents.stream()
    .collect(Collectors.toMap(                                // Collected to Map<Document, List<Document>>
        Function.identity(),                                  // Document is the key
        d1 -> documents.stream()                              // Value are the qualified documents
            .filter(d2 -> !d1.equals(d2) &&            
                utils.matchesOnCriteria(d1,d2, userCriteria)
            .collect(Collectors.toList())));                  // ... as List<Document>

Set<Pair<Document>> matches = map.entrySet().stream().reduce( // Reduce the Entry<Dokument, List<Document>>
    new HashSet<>(),                                          // ... to Set<Pair<>>
    (set, e) -> {
        set.addAll(e.getValue().stream()                      // ... where is
            .map(v -> new Pair<Document>(e.getKey(), v))      // ... the Pair of qualified documents
            .collect(Collectors.toSet()));                   
        return set;
    },
    (left, right) -> { left.addAll(right); return left; });   // Merge operation

条件!matches.contains(p)是多余的,有更好的方法来确保不同的值。使用Stream::distinct或收集Set作为无序独特集合的流。

在Baeldung's阅读更多内容:删除所有重复项。


推荐阅读