LinkedHashMap entrySet 的顺序未保留在流中 (Android)



我正在为注册屏幕创建一个非常简单的表单验证实用程序,并且我遇到了一些有关LinkedHashMap的意外行为,以及从其entrySet创建的流。

我正在存储验证结果LinkedHashMap,并进行以下语句排序:

Map<ValidationResult.SignUpField, Boolean> fieldStatuses = new LinkedHashMap<>();
fieldStatuses.put(EMAIL, isValidEmail(emailAddress));
fieldStatuses.put(USERNAME, isValidUsername(username));
fieldStatuses.put(BIRTHDAY, isValidBirthday(birthday));
fieldStatuses.put(PASSWORD, isValidPassword(password));
fieldStatuses.put(CONFIRM_PASSWORD, password.equals(confirmedPassword));
List<ValidationEntry> invalidFields = aggregateInvalidFields(fieldStatuses);

除"确认密码"外,一种特定的迭代使上述所有字段无效。在输入集上使用简单的循环并省略有效结果,该结果将按以下顺序显示:

  • 电子邮件
  • 用户名
  • 生日
  • 密码
然后,我尝试使用Android上的流API的子集(针对版本25的分钟为19,因此缺乏Collectors.toMap()):
private static List<ValidationEntry> aggregateInvalidFields(Map<ValidationResult.SignUpField, Boolean> fields) {
    List<ValidationEntry> invalidFields = new ArrayList<>();
    fields.entrySet()
            .stream()
            .filter(entry -> !entry.getValue())
            .forEachOrdered(entry -> {
                ValidationResult.SignUpField field = entry.getKey();
                invalidFields.add(new ValidationEntry(field, lookUpErrorCode(field)));
            });
    return invalidFields;
}

但是该代码得出以下顺序:

  • 生日
  • 密码
  • 用户名
  • 电子邮件

这里到底发生了什么,为什么流的结果不兑现LinkedHashMap的插入顺序?请注意,如果我将forEachOrderedforEach交换,它仍然不会插入。

此行为是Android的7.0/7.1 LinkedHashmap的实现中的已知错误。

linkedhashmap的收集视图 entrySetvalueskeySet正确地报告它们是 ORDERED,但实际上不是因为在内部使用了父类的分配器实现(HashMap)。

这种行为已经在Javadoc中进行了记录,并且在那里也提出了解决方法。

修复程序已于2016-08-16进行,并将出现在下一个Android版本中。

为了澄清:Google在2017-01首先意识到了这个错误,因此上面提到的"修复程序"是偶然的解决方案。如果他们早些时候知道这个问题,则该分辨率将包括在7.1

最新更新