Почему цикл forEach потока Java 8 дублирует результаты после использования метода sorted () - PullRequest
1 голос
/ 29 марта 2019

Во время упражнений с анаграммами я обнаружил, что мой код после выполнения имеет дубликаты на выходе в зависимости от момента использования метода sorted() из Stream класса.

Когда я делаю сортировку перед filter() и forEach() методы

        words.stream()
                .sorted()
                .filter(s1 -> !alreadyFound.contains(s1) && words.stream()
                        .filter(s2 -> isAnagram.apply(s1, s2))
                        .count() == maxAnagrams)
                .forEach(s1 -> {

Это дает такие результаты:

 abel able bale bela elba
 alger glare lager large regal
 angel angle galen glean lange
 caret carte cater crate trace
 elan lane lean lena neal
 evil levi live veil vile

Но когда я использую sorted() после filter() и до forEach() методов

        words.stream()
                .filter(s1 -> !alreadyFound.contains(s1) && words.stream()
                        .filter(s2 -> isAnagram.apply(s1, s2))
                        .count() == maxAnagrams)
                .sorted()

Затем он дает такие результаты:

 abel able bale bela elba
 abel able bale bela elba
 alger glare lager large regal
 angel angle galen glean lange
 angel angle galen glean lange
 abel able bale bela elba
 abel able bale bela elba
 caret carte cater crate trace
 caret carte cater crate trace
 caret carte cater crate trace
 caret carte cater crate trace
 elan lane lean lena neal
 abel able bale bela elba
 evil levi live veil vile
 angel angle galen glean lange
 alger glare lager large regal
 angel angle galen glean lange
 alger glare lager large regal
 elan lane lean lena neal
 angel angle galen glean lange
 alger glare lager large regal
 elan lane lean lena neal
 elan lane lean lena neal
 evil levi live veil vile
 evil levi live veil vile
 elan lane lean lena neal
 alger glare lager large regal
 caret carte cater crate trace
 evil levi live veil vile
 evil levi live veil vile

Кажется, что во втором подходе программа дублирует результаты и добавляет уже найденные слова в вывод.Интересно, почему это происходит?

Я использую:

jdk1.8.0_201

Пример кода:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main2 {
    public static void main(String[] args) {
        List<String> words = new ArrayList<>();
        List<String> alreadyFound = new ArrayList<>();

        BiFunction<String, String, Boolean> isAnagram = (s1, s2) -> {
            if (s1.length() != s2.length()) return false;
            char[] c1 = s1.toCharArray();
            char[] c2 = s2.toCharArray();
            Arrays.sort(c1);
            Arrays.sort(c2);
            return Arrays.equals(c1, c2);
        };

        try (InputStream inputStream = new URL("http://wiki.puzzlers.org/pub/wordlists/unixdict.txt").openStream();
             InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
             Stream<String> stream = new BufferedReader(inputStreamReader).lines()) {
            stream.forEach(words::add);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        long maxAnagrams = Collections.max(words.stream()
                .map(s1 -> words.stream()
                        .filter(s2 -> isAnagram.apply(s1, s2))
                        .count())
                .collect(Collectors.toList())
        );
        words.stream()
//                .sorted()
                .filter(s1 -> !alreadyFound.contains(s1) && words.stream()
                        .filter(s2 -> isAnagram.apply(s1, s2))
                        .count() == maxAnagrams)
//                .sorted()
                .forEach(s1 -> {
                    alreadyFound.add(s1);
                    words.stream()
                            .filter(s2 -> isAnagram.apply(s1, s2))
                            .forEach(s2 -> {
                                alreadyFound.add(s2);
                                System.out.print(" " + s2);
                            });
                    System.out.println();
                });
    }
}

// РЕДАКТИРОВАТЬ: ВЫКЛЮЧЕНА ТЕМА Я считаю, что это лучший способ достичь желаемых результатов:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main4 {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        try (InputStream inputStream = new URL("http://wiki.puzzlers.org/pub/wordlists/unixdict.txt").openStream();
             InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
             Stream<String> stream = new BufferedReader(inputStreamReader).lines()) {
            List<List<String>> anagrams = new ArrayList<>(stream
                    .collect(Collectors.groupingBy(o -> {
                        char[] chars = o.toCharArray();
                        Arrays.sort(chars);
                        return new String(chars);
                    }))
                    .values());
            int maxAnagrams = anagrams.parallelStream()
                    .mapToInt(List::size)
                    .max()
                    .orElse(0);
            anagrams.stream()
                    .filter(strings -> strings.size() == maxAnagrams)
                    .sorted(Comparator.comparing(o -> o.get(0)))
                    .forEach(strings -> {
                        strings.forEach(s -> System.out.print(s + " "));
                        System.out.println();
                    });
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        long stop = System.currentTimeMillis();
        System.out.println(stop - start);
    }
}

1 Ответ

0 голосов
/ 29 марта 2019

Я не уверен на 100%, но я полагаю, что причина такого поведения в зависимости от того, где вы сортируете свой поток, заключается в «синхронизации» данных, проходящих через поток.

Ваш foreach добавляет их в список слов, которые уже были найдены, но имейте в виду, что никакие данные не оцениваются, пока не будет вызван foreach.Это означает, что элементы добавляются в список только тогда, когда весь набор проходит через foreach.Ваш основной фильтр, который происходит до foreach, вероятно, увидит некоторое дублирование в результате попытки фильтрации по списку, которого на самом деле еще не существует.

Сортировка - это гаечный ключ в передачах всего этого, потому чтоон может задержать поток данных - нельзя гарантировать сортировку списка до тех пор, пока не будет просмотрен каждый элемент.Это не совсем завершающая операция, но она задерживает события до тех пор, пока не пройдут все элементы.Ленивая оценка - замечательная вещь, но она может быть сложной, и часто вам действительно нужно быть уверенным в том, что сортировка - это то, что вы хотите, прежде чем идти по этому пути.Пытаясь избежать, есть лучшие способы сделать это (у потока есть функция .distinct(), которая сделает это для вас).

Я написал еще одну реализацию того, что вы пытаетесь сделать здесьэто дает следующий результат.

abel able bale bela elba 
alger glare lager large regal 
angel angle galen glean lange 
caret carte cater crate trace 
elan lane lean lena neal 
evil levi live veil vile 

Если вы хотите, я могу поделиться с вами этим - но у меня сложилось впечатление, что это для какой-то природы задания, и вы копаете, чтобы выяснить это на вашемсвоя.Хорошо, если это так.

Если вы хотите получить несколько советов или задать вопросы кому-либо, не стесняйтесь написать мне DM, и я буду рад помочь.После этого я отредактирую этот пост и опубликую код, который использовал для генерации блока выше.

...