LinkedHashMap entrySet 的顺序未保留在流中 (Android)
LinkedHashMap entrySet's order not being preserved in a stream (Android)
我正在为注册屏幕创建一个非常简单的表单验证实用程序,我 运行 遇到了一些关于 LinkedHashMap
和从其 [=14= 创建的流的意外行为].
我将验证结果存储在 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);
一个特定的迭代产生除 "confirm password" 之外的所有上述字段无效。在条目集上使用简单的 for 循环并忽略有效结果,无效结果按以下顺序出现:
- 电子邮件
- 用户名
- 生日
- 密码
然后我尝试使用 Android 上可用的 Stream 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
的插入顺序?请注意,如果我将 forEachOrdered
换成 forEach
,它仍然不是插入顺序。
此行为是 Android 的 LinkedHashMap 7.0 / 7.1 实现中的一个已知错误。
entrySet
、values
和 keySet
的 LinkedHashMap 集合视图的拆分器正确报告它们是 ORDERED
但实际上它们不是,因为拆分器实现父级 class (HashMap) 在内部使用。
此行为已在 Javadoc 中documented,那里也提出了解决方法。
fix 已于 2016-08-16 提交,将出现在下一个 Android 版本中。
澄清一下:Google 最初是在 2017-01 年才意识到这个错误,所以上面提到的 "fix" 是一个意外的修复。如果他们早点知道这个问题,该解决方案将包含在 7.1
我正在为注册屏幕创建一个非常简单的表单验证实用程序,我 运行 遇到了一些关于 LinkedHashMap
和从其 [=14= 创建的流的意外行为].
我将验证结果存储在 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);
一个特定的迭代产生除 "confirm password" 之外的所有上述字段无效。在条目集上使用简单的 for 循环并忽略有效结果,无效结果按以下顺序出现:
- 电子邮件
- 用户名
- 生日
- 密码
然后我尝试使用 Android 上可用的 Stream 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
的插入顺序?请注意,如果我将 forEachOrdered
换成 forEach
,它仍然不是插入顺序。
此行为是 Android 的 LinkedHashMap 7.0 / 7.1 实现中的一个已知错误。
entrySet
、values
和 keySet
的 LinkedHashMap 集合视图的拆分器正确报告它们是 ORDERED
但实际上它们不是,因为拆分器实现父级 class (HashMap) 在内部使用。
此行为已在 Javadoc 中documented,那里也提出了解决方法。
fix 已于 2016-08-16 提交,将出现在下一个 Android 版本中。
澄清一下:Google 最初是在 2017-01 年才意识到这个错误,所以上面提到的 "fix" 是一个意外的修复。如果他们早点知道这个问题,该解决方案将包含在 7.1