Понимание порядка элементов в потоке, сгенерированном из HashSet

Я прочитал официальные документы Java 8:

Потоки могут иметь или не иметь определенный порядок встречи. Наличие у потока порядка встреч зависит от источника и промежуточных операций. Некоторые источники потоков (например, List или массивы) упорядочены по своей сути, а другие (например, HashSet) — нет.
Если поток упорядочен, повторное выполнение идентичных потоковых конвейеров на идентичном источнике даст идентичный результат; если он не упорядочен, повторное выполнение может привести к другим результатам.

Пытался понять упомянутое поведение через этот код

public class StreamOrderValidator
{
    public static void main( String[] args )
    {
        String[] colors=new String[] {"red","green","blue","orange"};
        List<String> colorsList=Arrays.asList(colors);

        HashSet<String> colorsSet=new HashSet<>();
        colorsSet.addAll(colorsList);
        System.out.println(colorsSet);            // [red, orange, green, blue]

        List<String> processedColorsSet = processStream(colorsSet.stream());
        System.out.println(processedColorsSet);   // [RED, ORANGE, GREEN, BLUE]
    }

    private static List<String> processStream(Stream<String> colorStream) {
        List<String> processedColorsList = colorStream.filter(s->s.length()<=6).
                map(String::toUpperCase).collect(Collectors.toList());
        return processedColorsList;
    }
}

Я запускал этот код много раз, и порядок элементов в результирующем потоке всегда был одинаковым (отображается как комментарий). Я не могу понять, как это оправдывает приведенный выше текст о том, что «Порядок не сохраняется для неупорядоченной коллекции».

Я определенно неправильно понимаю извлеченный текст из javadocs.


person user2653926    schedule 27.08.2017    source источник
comment
Я считаю, что ответ здесь stackoverflow.com/questions/29216588/ может просто охватывать то, что вы здесь спрашиваете.   -  person Naman    schedule 27.08.2017


Ответы (3)


Здесь действительно есть небольшое недоразумение. HashSet или любой Set не относится к порядку, если только TreeSet не упорядочен на основе Comparator.

На данный момент в java-8 после того, как вы поместите элементы в HashSet (и не измените его) - будет порядок расположения элементов; но опять же, при условии, что вы не добавляете и не удаляете ни один из них. Это может измениться в любой момент времени, так что не полагайтесь на это.

Например, запустив это:

 String[] colors = new String[] { "red", "green", "blue", "orange" };
 List<String> colorsList = Arrays.asList(colors);

 HashSet<String> colorsSet = new HashSet<>();
 colorsSet.addAll(colorsList);
 System.out.println(colorsSet);

Независимо от того, сколько раз в java-8 на данный момент вы всегда получите один и тот же вывод:

[red, orange, green, blue]

Но как только вы сделаете некоторую внутреннюю перетасовку:

    for (int i = 0; i < 1000; ++i) {
        colorsSet.add("" + i);
    }

    for (int i = 0; i < 1000; ++i) {
        colorsSet.remove("" + i);
    }   


    System.out.println(colorsSet); // [blue, red, green, orange]

Вы можете видеть, что выходные данные меняются, потому что Sets не имеют порядка. Ключевым моментом является то, что порядка нет, тот факт, что вы видите порядок, не является гарантией того, что он будет происходить каждый раз - в java-8 может быть сборка, которая нарушит этот порядок. И на самом деле это легко наблюдать, например, с java-9, где есть шаблон рандомизации для новых Set.

Если вы запустите это несколько раз, результат будет отличаться:

 Set<String> set = Set.of("red", "green", "blue", "orange");
 System.out.println(set);

Таким образом, очевидно, что для вас stream из такого Set порядок не будет гарантирован, и поэтому вы действительно увидите разные результаты от запуска к запуску.

person Eugene    schedule 28.08.2017

То, что вы видите, в основном является удачей, поскольку HashSet, который вы передаете, возвращает значения в последовательном порядке. Если вы добавили достаточное количество значений с течением времени, вы в конечном итоге увидите разные результаты из потока из-за того, что основной HashMap HashSet должен изменить свой размер и изменить порядок.

То, что вы предоставили (четыре цвета), случайно будет возвращать одни и те же результаты каждый раз, поскольку базовому HashMap нет необходимости изменять свой размер и переупорядочивать значения.

Принимая во внимание, что HashSet поддерживается HashMap в соответствии с документами API Java, этот вопрос и его принятый ответ охватывают то, что вы видите, благодаря объяснению поведения HashMap:

Порядок значений, полученных из HashMap

person Sam Abazly    schedule 27.08.2017

повторное выполнение может привести к другим результатам.

Вот это might слово. Даже если это не гарантирует порядок, это не значит, что порядок каждый раз будет случайным. Элементы размещаются на основе hashcode. Попробуйте другие значения:

    String[] colors=new String[] {"5reegdfg","fsdfsd6546","fsdfxvc4","77ggg"};
    List<String> colorsList=Arrays.asList(colors);

    HashSet<String> intSet =new HashSet<>();
    intSet.addAll(colorsList);


    intSet.forEach(e -> System.out.print(e + " "));

    System.out.println();
    intSet.add("fvcxbxb78ok");


    intSet.forEach(e -> System.out.print( e + " "));

Вывод таков:

fsdfxvc4 5reegdfg 77ggg fsdfsd6546 
fsdfxvc4 fvcxbxb78ok 5reegdfg 77ggg fsdfsd6546 

Как видите, порядок в этом примере другой.

person Schidu Luca    schedule 27.08.2017
comment
Я так думаю... forEach не очень хороший пример. Документы Java поясняют, что некоторые терминальные операции могут игнорировать порядок встреч, например forEach(), взятый из раздела упорядочения docs.oracle.com/javase/8/docs/api/java/util/stream/. - person user2653926; 27.08.2017