I'm creating a very simple form validation utility for a sign up screen, and I'm running into some unexpected behavior concerning LinkedHashMap
and a stream created from its entrySet
.
I'm storing validation results in a LinkedHashMap
, with the following ordering of statements:
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);
One particular iteration yields all of the above fields invalid except for "confirm password". Using a simple for loop over the entry set and omitting valid results, the invalid results appear in the following order:
- Username
- Birthday
- Password
I then attempted to make use of the subset of Stream API's available on Android (targeting version 25 with a min of 19, hence the lack of 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;
}
But that code yields the following order:
- Birthday
- Password
- Username
What exactly is happening here, why is the result of the stream not honoring the insertion order of the LinkedHashMap
? Note that if I swap out forEachOrdered
with forEach
, it's still not insertion ordered.