Java 8-Collections.groupingBy结果顺序

Java 8-Collections.groupingBy结果顺序,java,java-8,Java,Java 8,我正在准备Java考试,有一个问题让我很为难。尽管我努力学习,但还是无法找出是什么决定了结果的顺序 请看一看: class Country { public enum Continent { ASIA, EUROPE } String name; Continent region; public Country(String na, Continent reg) { name = na; region =

我正在准备Java考试,有一个问题让我很为难。尽管我努力学习,但还是无法找出是什么决定了结果的顺序

请看一看:

class Country {

    public enum Continent {
        ASIA, EUROPE
    }
    String name;
    Continent region;

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

    public String getName() {
        return name;
    }

    public Continent getRegion() {
        return region;
    }
}

public class OrderQuestion {

    public static void main(String[] args) {
        List<Country> couList = Arrays.asList(
                new Country("Japan", Country.Continent.ASIA),
                new Country("Italy", Country.Continent.EUROPE),
                new Country("Germany", Country.Continent.EUROPE));
        Map<Country.Continent, List<String>> regionNames = couList.stream()
                .collect(Collectors.groupingBy(Country::getRegion,
                        Collectors.mapping(Country::getName, Collectors.toList())));
        System.out.println(regionNames);
    }
}
国家级{
欧洲大陆公共普查{
亚洲、欧洲
}
字符串名;
大陆区域;
公共国家(字符串na,大陆注册){
name=na;
region=reg;
}
公共字符串getName(){
返回名称;
}
公共区域(){
返回区;
}
}
公共类排序问题{
公共静态void main(字符串[]args){
List couList=Arrays.asList(
新国家(“日本”、国家、大陆、亚洲),
新国家(“意大利”、国家、大陆、欧洲),
新国家(“德国”、国家、大陆、欧洲”);
Map regionNames=couList.stream()
.collect(收集器).groupingBy(国家::getRegion,
Collectors.mapping(Country::getName,Collectors.toList());
System.out.println(区域名称);
}
}
结果如何

A.{欧洲=[意大利、德国],亚洲=[日本]}

B.{亚洲=[日本]、欧洲=[意大利、德国]}

C.{欧洲=[德国、意大利]、亚洲=[日本]}

D.{欧洲=[德国]、欧洲=[意大利]、亚洲=[日本]}


最重要的是什么决定了具体的结果而不是另一个结果?

我们可以消除
D
,因为Map中的键需要是唯一的,这对于
欧洲
来说是失败的

我们可以取消
C
,因为
[德国、意大利]
的订单<代码>意大利在列表中位于
德国
之前,因此在结果列表中也必须按该顺序存储

但是,我们应该如何决定是消除
B
还是消除
A
嗯,我们不能

Map不保证键值对的特定顺序。有些映射允许记住键值对的放置顺序,如LinkedHashMap,有些映射允许按键对条目进行排序,如TreeMap,但这一行为不适用于
收集器。groupingBy

事实证明,此方法使用的是
HashMap
,它根据键(
Country.Continent
enum)的hashCode()和已持有的对数对键值对进行排序。Enum的
hashCode()
的实现是从对象类继承的,这意味着它基于内存位置,当我们运行JVM时,内存位置每次都会发生变化,因此它是随机值,防止我们采用任何顺序(这确认了它是未指定的)


因此,基于对
groupingBy
返回的Map缺乏规范,两个条目的顺序都是可能的,因此A和B都是可能的答案。

查看
Map
的API文档,看看它对元素顺序的说明,建议的答案是:A.{EUROPE=[意大利,德国],ASIA=[日本]}B.{ASIA=[日本],欧洲=[意大利,德国]}C.{欧洲=[德国,意大利]、亚洲=[日本]}D.{欧洲=[德国]、欧洲=[意大利]、亚洲=[日本]}我知道是什么产生了执行此代码的Java运行时(A),但不知道这是为什么,也没有什么不同。我在寻找基本原理。使用to streams,您可以在一行代码中执行许多操作。为了让问题更清楚,你可以分割流线,把每一步都分配给一个变量,并描述出你真正感到惊讶的事情,以及你所看到的事情。在我看来,这道试题似乎相当荒谬。如果我没有弄错的话,
Arrays.asList
collector.groupingBy
不能保证底层实现,因此也不能保证元素的顺序。即使他们这样做了,我也不会假设任何事情,除非使用sort@IcedDante保证顺序,等等:返回由指定数组支持的固定大小的列表。(对返回列表的更改“写入”到数组。)[…]返回的列表可序列化,并实现
RandomAccess
。分析良好,+1。在我看来,OP描述的问题是一个糟糕的考试问题。获得“正确”答案依赖于实现特定的行为(HashMap排序),而不是规范。@FredFilozof(a)我怀疑这个问题的作者只是简单地运行了几次代码,得到了相同的答案,所以他认为它必须总是正确的。但我通过在使用enum之前创建额外的对象来获得这两个答案,因此它们将占用与先前授予的内存不同的内存,因此它们的哈希也会发生变化。(b) 此处流中元素的顺序取决于源代码中元素的顺序(在
couList
中)。HashMap确实不保证任何特定的顺序,但有一些顺序是基于密钥的hashcode和对的数量。如果密钥的hashcode是使用在每次运行中可以更改的属性(如内存地址)计算的,则在每次运行中使用此类密钥的HashMap可能具有半随机顺序。但许多类的hashCode不是基于属性的,每次运行都可能是随机的。例如,Integer返回它所持有的hashCode int值,因此
新整数(1)
的hashCode是
1
,而
新整数(2)
的hashCode是'2'。字符串的HashCode也仅根据它所包含的字符进行计算。所以,若在HashMap中使用Integer作为hey,那个么对于相同的键组合,应该获得相同的顺序。顺便说一句,HashMap中的顺序可能取决于放置元素的顺序。HashMap由类似于链表(我们称之为bucket)的结构数组进行备份。每个bucket将保存特定范围的hashcode的元素(比如,如果bucket的数量是4,那么第一个bucket可以使用hash 0、4、8等来收集元素,第二个bucket使用1、5、9,您就知道了)。所以bucket中元素的顺序取决于插入的顺序。所以如果两个键可以有相同的hashCode,比如
“Aa”