了解从HashSet生成的流中元素的顺序

时间:2017-08-27 17:54:13

标签: java java-8 java-stream hashset

我读了这篇Java 8官方文档:

  

Streams可能有也可能没有已定义的遭遇顺序。是否   一个流有一个遭遇顺序取决于源和   中间操作。某些流源(例如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提取的文本。

3 个答案:

答案 0 :(得分:5)

这里确实存在一些误解。 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]

您可以看到输出更改,因为Set没有订单。 要点是没有顺序,事实上你确实看到订单并不是每次都发生的保证 - 在java-8中可能会有一个破坏这个顺序的构建。事实上,例如java-9很容易观察到 - 新的Set存在随机化模式。

如果多次运行,结果会有所不同:

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

很明显,stream来自Set这样的订单将无法得到保证,因此您确实会看到不同的运行结果。

答案 1 :(得分:4)

您所看到的基本上是运气,您正在流式传输的HashSet按顺序返回值。如果随着时间的推移添加了足够的值,最终会看到来自流的不同结果,因为HashSet的底层HashMap必须调整自身并重新排序。

你提供的(四种颜色)偶然会每次都返回相同的结果,因为底层的HashMap不需要调整自身的大小并重新排序值。

请注意,根据Java API文档,HashSet由HashMap支持,此问题及其接受的答案涵盖了您通过解释HashMap的行为而看到的内容:

Order of values retrieved from a HashMap

答案 2 :(得分:2)

  

重复执行可能会产生不同的结果。

这个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 

如您所见,此示例中的顺序不同。