我正在为注册屏幕创建一个非常简单的表单验证实用程序,并且我遇到了一些有关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);
除"确认密码"外,一种特定的迭代使上述所有字段无效。在输入集上使用简单的循环并省略有效结果,该结果将按以下顺序显示:
- 电子邮件
- 用户名
- 生日
- 密码
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
的插入顺序?请注意,如果我将forEachOrdered
与forEach
交换,它仍然不会插入。
此行为是Android的7.0/7.1 LinkedHashmap的实现中的已知错误。
linkedhashmap的收集视图 entrySet
, values
和 keySet
正确地报告它们是 ORDERED
,但实际上不是因为在内部使用了父类的分配器实现(HashMap)。
这种行为已经在Javadoc中进行了记录,并且在那里也提出了解决方法。
修复程序已于2016-08-16进行,并将出现在下一个Android版本中。
为了澄清:Google在2017-01首先意识到了这个错误,因此上面提到的"修复程序"是偶然的解决方案。如果他们早些时候知道这个问题,则该分辨率将包括在7.1