Java8:关于Collectors.groupingBy返回的Map元素的顺序

Java8: About order of Map elements returned by Collectors.groupingBy

想请教一下遇到的例子修改的下面这段代码

我想知道当输入的List元素序列发生变化时,结果Map中的输出序列是否会发生变化。但是,似乎所有情况下的输出都是相同的(我在 IDE 上重复 运行 程序)。我可以就生成的 Map 元素的顺序是否可以预期寻求建议? 使用 enum 或 String 时,序列是否会有任何差异(alphabetical/non-alphbetical?),或者元素序列只是随机的?

public class TestCountry {

    public enum Continent {ASIA, EUROPE}

    String name;
    Continent region;

    public TestCountry (String na, Continent reg) {
        name = na;
        region = reg;
    }

    public String getName () {
        return name;
    }

    public Continent getRegion () {
        return region;
    }

    public static void main(String[] args) {
        List<TestCountry> couList1 = Arrays.asList (
                new TestCountry ("Japan", TestCountry.Continent.ASIA),
                new TestCountry ("Italy", TestCountry.Continent.EUROPE),
                new TestCountry ("Germany", TestCountry.Continent.EUROPE));

        List<TestCountry> couList2 = Arrays.asList (
                new TestCountry ("Italy", TestCountry.Continent.EUROPE),
                new TestCountry ("Japan", TestCountry.Continent.ASIA),
                new TestCountry ("Germany", TestCountry.Continent.EUROPE));

        Map<TestCountry.Continent, List<String>> mapWithRegionAsKey1 = couList1.stream ()
                    .collect(Collectors.groupingBy (TestCountry ::getRegion,
                        Collectors.mapping(TestCountry::getName, Collectors.toList())));

        Map<String, List<Continent>> mapWithNameAsKey1 = couList1.stream ()
                    .collect(Collectors.groupingBy (TestCountry::getName,
                        Collectors.mapping(TestCountry::getRegion, Collectors.toList())));

        Map<TestCountry.Continent, List<String>> mapWithRegionAsKey2 = couList2.stream ()
                    .collect(Collectors.groupingBy (TestCountry ::getRegion,
                        Collectors.mapping(TestCountry::getName, Collectors.toList())));

        Map<String, List<Continent>> mapWithNameAsKey2 = couList2.stream ()
                    .collect(Collectors.groupingBy (TestCountry::getName,
                        Collectors.mapping(TestCountry::getRegion, Collectors.toList())));

        System.out.println("Value of mapWithRegionAsKey1 in couList1: " + mapWithRegionAsKey1);
        System.out.println("Value of mapWithNameAsKey1 in couList1: " + mapWithNameAsKey1);

        System.out.println("Value of mapWithRegionAsKey2 in couList2: " + mapWithRegionAsKey2);
        System.out.println("Value of mapWithNameAsKey2 in couList2: " + mapWithNameAsKey2);
    }
}

/*
 * Output:
Value of mapWithRegionAsKey1 in couList1: {ASIA=[Japan], EUROPE=[Italy, 
Germany]}
Value of mapWithNameAsKey1 in couList1: {Japan=[ASIA], Italy=[EUROPE], 
Germany=[EUROPE]}
Value of mapWithRegionAsKey2 in couList2: {ASIA=[Japan], EUROPE=[Italy, 
Germany]}
Value of mapWithNameAsKey2 in couList2: {Japan=[ASIA], Italy=[EUROPE], 
Germany=[EUROPE]}
 */

Collectors.groupingBy() 目前 returns 一个 HashMap。这是在未来版本中可能会更改的实现细节。

HashMap 的条目未排序,但它们的迭代顺序(打印 HashMap 时)是确定的,不依赖于插入顺序。

因此,无论输入中元素的顺序如何,您都会看到相同的输出 List。不过,您不应该依赖该顺序,因为它是一个实现细节。

如果您关心输出中条目的顺序 Map,您可以修改代码以生成 LinkedHashMap(默认顺序为插入顺序)或 TreeMap(键排序的地方)。

顺便说一句,如果您将输入更改为

    List<TestCountry> couList1 = Arrays.asList (
            new TestCountry ("Japan", TestCountry.Continent.ASIA),
            new TestCountry ("Italy", TestCountry.Continent.EUROPE),
            new TestCountry ("Germany", TestCountry.Continent.EUROPE));

    List<TestCountry> couList2 = Arrays.asList (
            new TestCountry ("Germany", TestCountry.Continent.EUROPE),
            new TestCountry ("Italy", TestCountry.Continent.EUROPE),
            new TestCountry ("Japan", TestCountry.Continent.ASIA)
            );

您将在输出中得到不同的顺序:

Value of mapWithRegionAsKey1 in couList1: {ASIA=[Japan], EUROPE=[Italy, Germany]}
Value of mapWithNameAsKey1 in couList1: {Japan=[ASIA], Italy=[EUROPE], Germany=[EUROPE]}
Value of mapWithRegionAsKey2 in couList2: {ASIA=[Japan], EUROPE=[Germany, Italy]}
Value of mapWithNameAsKey2 in couList2: {Japan=[ASIA], Italy=[EUROPE], Germany=[EUROPE]}

这是因为单个输出 Lists(在当前实现中是 ArrayLists)的元素是根据插入顺序排序的,插入顺序取决于你的输入 List.

在质疑这个之前,请阅读文档,内容如下:

There are no guarantees on the type, mutability, serializability, or thread-safety of the Map or List objects returned.

因此,如果您无法判断返回的 Map 的类型,您就无法真正判断将以何种顺序(如果有)。那么目前,HashMap,但这并不能保证。

问题是你为什么要关心这个?如果是出于学术目的,那是有道理的,如果不是,收集到一些确实保证顺序的东西。

Collectors.groupingBy 接受一个参数,允许您指定您可能需要的实际 Map 实现。