Использование Java 8 потоков для достижения sh удаления петель - PullRequest
4 голосов
/ 16 января 2020

У меня есть список объектов документов, которые необходимо сопоставить на основе определенных критериев. Существует служебная функция, которая берет любые 2 типа документов и определяет, соответствуют ли они по ряду критериев, например, по жанру документа, разделяют ли они авторов и т. Д. c. Код работает, но я хотел бы использовать 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);
               }
             }
           }
          } 
        }  
     }

Как я могу сделать это с помощью потоков?

1 Ответ

3 голосов
/ 16 января 2020

Идея следующего решения с использованием 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). Условие Pair должно переопределять как Object::equals, так и Object:hashCode и обеспечивать равенство на основе обоих представленных документов.

      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: удалить все дубликаты .

...