使用流从 List 生成 Map 的优雅方法是什么
What is the elegant way to generate Map from List using streams
我有从数据库中获取的学生列表。我将谓词应用于列表,并用有效、无效的学生对列表进行分区。现在对于无效的学生,我想生成以学生为键和错误消息为值的地图。因为我需要为我正在做的每个 student.Here 生成报告,但我不知道这是否是一个好方法,或者是否有更好的方法。
实际上,在获得无效学生列表后,我正在尝试创建一个函数,但我认为代码变得混乱,可能有更好的方法来实现它。这是我在做什么
private List<Predicate<OlccStudent>> getAllPredicates() {
List<Predicate<OlccStudent>> allPredicates = Arrays.asList(
isValidFirstName(),
isValidMiddleInitial(),
isValidLastName(),
isValidStreetAddress(),
isValidCity(),
isValidState(),
isValidZip(),
isValidDateOfBirth(),
isValidSsn(),
isValidTestDate(),
isValidTestAnswers(),
isValidProviderCode(),
isValidInstructorCode()
);
return allPredicates;
}
public Map<Boolean, List<OlccStudent>> getStudentsMap(List<OlccStudent> olccStudentsList) {
List<Predicate<OlccStudent>> allPredicates = getAllPredicates();
Predicate<OlccStudent> compositePredicate = allPredicates.stream()
.reduce(w -> true, Predicate::and);
Map<Boolean, List<OlccStudent>> studentsMap= olccStudentsList
.stream()
.collect(Collectors.partitioningBy(compositePredicate));
return studentsMap;
}
public Map<OlccStudent, String> getInvalidStudentsMap(Map<Boolean, List<OlccStudent>> studentsMap) throws Exception {
List<OlccStudent> invalidStudentsList =
studentsMap.entrySet()
.stream()
.filter(p -> p.getKey() == Boolean.FALSE)
.flatMap(p -> p.getValue().stream())
.collect(Collectors.toList());
Function<List<OlccStudent>, Map<OlccStudent, String>> invalidStudentFunction = list -> {
Map<OlccStudent, String> invalidStudentsMap = new LinkedHashMap<>();
list.forEach(student-> {
String errorMessage = getStudentErrorMessage(student);
invalidStudentsMap.put(student, errorMessage);
});
return invalidStudentsMap;
};
Map<OlccStudent, String> invalidStudentsMap = invalidStudentFunction.apply(invalidStudentsList);
return invalidStudentsMap;
return null;
}
private String getStudentErrorMessage(OlccStudent student) {
String firstName = student.getFirstName();
String middleInitial = student.getMiddleInitial();
String lastName = student.getLastName();
String streetAddress = student.getStreetAddress();
....
StringBuilder errorBuilder = new StringBuilder();
//The predicate 'negate' method returns a predicate that represents the logical negation or opposite
if (isValidFirstName().negate().test(student)) {
String error = "Invalid FirstName: " + firstName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidMiddleInitial().negate().test(student)) {
String error = "Invalid Middle Initial: " + middleInitial;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidLastName().negate().test(student)) {
String error = "Invalid LastName: " + lastName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidStreetAddress().negate().test(student)) {
String error = "Invalid StreetAddress: " + streetAddress;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
...
if (errorBuilder.length() > 0) {
errorBuilder.deleteCharAt(errorBuilder.length() - 1);
}
return errorBuilder.toString().trim();
}
实际上,我对从 list.forEach()
调用的 getStudentErrorMessage()
感到困惑。我知道收藏家为您提供 Collectors.joining
功能。实际上我想以谓词的方式来做。就像我创建了所有谓词的列表,然后在流中使用它一样。我可以对我的错误消息做类似的事情吗?就像我从 invalid students List
创建流并使用 Collectors.toMap()
一样,将我的 Student 作为键并将错误消息作为其值。
谢谢
编辑
--------------
public class OlccStudentPredicate {
public static Predicate<OlccStudent> isValidTestDate() {
return p -> isValidDate(p.getTestDate());
}
public static Predicate<OlccStudent> isValidDateOfBirth() {
return p -> isValidDate(p.getDateOfBirth());
}
...
private static boolean isValidDate(String date) {
boolean valid = false;
if (StringUtils.isNotBlank(date)) {
try {
LocalDate.parse(date, DateTimeFormatter.ofPattern("MM/dd/yyyy"));
valid = true;
} catch (DateTimeParseException e) {
}
}
return valid;
}
@Test
public void test() {
List<OlccStudent> olccStudentsList = getOlccStudentsList();
try {
Map<OlccStudent, String> map = getInvalidStudentsMap(olccStudentsList);
System.out.println();
//olccStudentService.getStudentsMap(olccStudentsList);
} catch (Exception e) {
e.printStackTrace();
}
}
private List<OlccStudent> getOlccStudentsList() {
List<OlccStudent> olccStudentsList = new ArrayList<>();
OlccStudent student1 = new OlccStudent(1L, 1L, "firstName1", "middleInitial1", "lastName1", "email1",
"streetAdress1", "city1", "state1", "1234", "01/22/2015", "phoneNumber1", "01/22/2015",
"123456789", "testForm1", "providerCode1", "instructorCode1", "surveyAnswer1",
"testIdentifier1", "testAnswers1");
OlccStudent student2 = new OlccStudent(2L, 2L, "firstName2", "middleInitial2", "lastName2", "email2",
"streetAdress2", "city2", "state2", "5678", "02/22/2015", "phoneNumber2", "02/22/2015",
"987654321", "testForm2", "providerCode2", "instructorCode2", "surveyAnswer2",
"testIdentifier2", "testAnswers2");
OlccStudent student3 = new OlccStudent(3L,3L, "firstName3", "middleInitial3", "lastName3", "email3",
"streetAdress3", "city3", "state3", "zip3", "testDate3", "phoneNumber3", "dateOfBirth3",
"socialSecurityNumber3", "testForm3", "providerCode3", "instructorCode3", "surveyAnswer3",
"testIdentifier3", "testAnswers3");
OlccStudent student4 = new OlccStudent(4L, 4L, "firstName4", "middleInitial4", "lastName4", "email4",
"streetAdress4", "city4", "state4", "zip4", "testDate4", "phoneNumber4", "dateOfBirth4",
"socialSecurityNumber4", "testForm4", "providerCode4", "instructorCode4", "surveyAnswer4",
"testIdentifier4", "testAnswers4");
olccStudentsList.add(student1);
olccStudentsList.add(student2);
olccStudentsList.add(student3);
olccStudentsList.add(student4);
return olccStudentsList;
}
private String validate(OlccStudent student) {
String firstName = student.getFirstName();
String middleInitial = student.getMiddleInitial();
String lastName = student.getLastName();
String streetAddress = student.getStreetAddress();
String city = student.getCity();
String state = student.getState();
String zip = student.getZip();
String dateOfBirth = student.getDateOfBirth();
String ssn = student.getSocialSecurityNumber();
String phoneNumber = student.getPhoneNumber();
String testDate = student.getTestDate();
String testForm = student.getTestForm();
String testAnswers = student.getTestAnswers();
String providerCode = student.getProviderCode();
String instructorCode = student.getInstructorCode();
StringBuilder errorBuilder = new StringBuilder();
//The predicate 'negate' method returns a predicate that represents the logical negation or opposite
if (isValidFirstName().negate().test(student)) {
String error = "Invalid FirstName: " + firstName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidMiddleInitial().negate().test(student)) {
String error = "Invalid Middle Initial: " + middleInitial;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidLastName().negate().test(student)) {
String error = "Invalid LastName: " + lastName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidStreetAddress().negate().test(student)) {
String error = "Invalid StreetAddress: " + streetAddress;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidCity().negate().test(student)) {
String error = "Invalid City: " + city;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidState().negate().test(student)) {
String error = "Invalid State: " + state;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidZip().negate().test(student)) {
String error = "Invalid Zip: " + zip;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidDateOfBirth().negate().test(student)) {
String error = "Invalid DateOfBirth: " + dateOfBirth;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidSsn().negate().test(student)) {
String error = "Invalid SSN: " + ssn;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidTestDate().negate().test(student)) {
String error = "Invalid TestDate: " + testDate;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidTestAnswers().negate().test(student)) {
String error = "Invalid TestAnswers: " + testAnswers;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidProviderCode().negate().test(student)) {
String error = "Invalid ProvideCode: " + providerCode;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidInstructorCode().negate().test(student)) {
String error = "Invalid InstructorCode: " + instructorCode;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (errorBuilder.length() > 0) {
errorBuilder.deleteCharAt(errorBuilder.length() - 1);
}
return errorBuilder.toString().trim();
}
public Map<OlccStudent, String> getInvalidStudentsMap(List<OlccStudent> studentsList) throws Exception {
Map<OlccStudent, String> map = studentsList.stream()
// Step 1: Validate each student, keeping a track of any error message generated.
.collect(Collectors.toMap(Function.identity(), student -> validate(student)))
// Step 2: Keep only those that have an error message associated.
.entrySet()
.stream()
.filter(entry -> entry.getValue() != null)
// Step 3: Generate a Map.
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
return map;
}
第 1 部分:从 Collection
个对象生成 Map
如果我对你的问题的理解正确,你想从 Collection
个对象生成一个 Map
,这样 Map
中的值是在验证对象时生成的错误消息Collection
和键是验证失败的实际对象。
因此,如果您的输入 Collection
是:
Student 1: Valid
Student 2: Valid
Student 3: Invalid
Student 4: Valid
Student 5: Invalid
预期输出为:
Student 3: {error message}
Student 5: {error message}
如果这是你想要的,你的问题就太复杂了,可以通过假设存在一个函数来简化,例如:
/**
* Validates a student and returns an error message if the student data is
* not valid. The error message provides the actual reason why the student
* data is invalid.
*
* @param student The student to validate.
* @return An error message if {@code student} contains invalid data,
* {@code null} otherwise.
*/
String validate(OlccStudent student) { ... }
现在,任务很简单。
// Step 0: We have a collection of students as an input.
Collection<OlccStudent> students = ...;
students.stream()
// Step 1: Validate each student, keeping a track of any error message generated.
.collect(Collectors.toMap(Function.identity(), student -> validate(student)))
// Step 2: Keep only those that have an error message associated.
.entrySet()
.stream()
.filter(entry -> entry.getValue() != null);
// Step 3: Generate a Map.
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
第 2 部分:验证对象
最好使用 JSR303 - Java 验证 API(及其实现之一,例如 Hibernate Validator 或 Apache Beans Validator)来验证 beans。这不仅是标准化的,而且需要更少的努力和维护。添加新验证很容易,整个框架与区域设置无关,允许生成特定于区域设置的消息。
我会创建一个 ValidationRule
class 来将验证谓词和错误消息格式化程序结合在一起:
static class ValidationRule {
public final Predicate<OlccStudent> predicate;
public final Function<OlccStudent, String> errorFormatter;
public ValidationRule(Predicate<OlccStudent> predicate,
Function<OlccStudent, String> errorFormatter) {
this.predicate = predicate;
this.errorFormatter = errorFormatter;
}
}
现在 getAllRules
将如下所示:
public static List<ValidationRule> getAllRules() {
return Arrays.asList(
new ValidationRule(isValidFirstName(), s -> "Invalid FirstName: " + s.getFirstName()),
new ValidationRule(isValidMiddleInitial(), s -> "Invalid Middle Initial: " + s.getMiddleInitial()),
new ValidationRule(isValidLastName(), s -> "Invalid LastName: " + s.getLastName()),
new ValidationRule(isValidStreetAddress(), s -> "Invalid StreetAddress: " + s.getStreetAddress())
// ...
);
}
并且您可以通过以下方式获取无效学生地图:
public Map<OlccStudent, String> getInvalidStudentsMap(List<OlccStudent> students) {
List<ValidationRule> rules = getAllRules();
return students
.stream()
.<Entry<OlccStudent, String>>map(student -> new AbstractMap.SimpleEntry<>(student, rules
.stream()
.filter(rule -> rule.predicate.test(student))
.map(rule -> rule.errorFormatter.apply(student))
.collect(Collectors.joining(ERROR_MESSAGE_SEPERATOR))))
.filter(entry -> !entry.getValue().isEmpty())
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
}
我有从数据库中获取的学生列表。我将谓词应用于列表,并用有效、无效的学生对列表进行分区。现在对于无效的学生,我想生成以学生为键和错误消息为值的地图。因为我需要为我正在做的每个 student.Here 生成报告,但我不知道这是否是一个好方法,或者是否有更好的方法。
实际上,在获得无效学生列表后,我正在尝试创建一个函数,但我认为代码变得混乱,可能有更好的方法来实现它。这是我在做什么
private List<Predicate<OlccStudent>> getAllPredicates() {
List<Predicate<OlccStudent>> allPredicates = Arrays.asList(
isValidFirstName(),
isValidMiddleInitial(),
isValidLastName(),
isValidStreetAddress(),
isValidCity(),
isValidState(),
isValidZip(),
isValidDateOfBirth(),
isValidSsn(),
isValidTestDate(),
isValidTestAnswers(),
isValidProviderCode(),
isValidInstructorCode()
);
return allPredicates;
}
public Map<Boolean, List<OlccStudent>> getStudentsMap(List<OlccStudent> olccStudentsList) {
List<Predicate<OlccStudent>> allPredicates = getAllPredicates();
Predicate<OlccStudent> compositePredicate = allPredicates.stream()
.reduce(w -> true, Predicate::and);
Map<Boolean, List<OlccStudent>> studentsMap= olccStudentsList
.stream()
.collect(Collectors.partitioningBy(compositePredicate));
return studentsMap;
}
public Map<OlccStudent, String> getInvalidStudentsMap(Map<Boolean, List<OlccStudent>> studentsMap) throws Exception {
List<OlccStudent> invalidStudentsList =
studentsMap.entrySet()
.stream()
.filter(p -> p.getKey() == Boolean.FALSE)
.flatMap(p -> p.getValue().stream())
.collect(Collectors.toList());
Function<List<OlccStudent>, Map<OlccStudent, String>> invalidStudentFunction = list -> {
Map<OlccStudent, String> invalidStudentsMap = new LinkedHashMap<>();
list.forEach(student-> {
String errorMessage = getStudentErrorMessage(student);
invalidStudentsMap.put(student, errorMessage);
});
return invalidStudentsMap;
};
Map<OlccStudent, String> invalidStudentsMap = invalidStudentFunction.apply(invalidStudentsList);
return invalidStudentsMap;
return null;
}
private String getStudentErrorMessage(OlccStudent student) {
String firstName = student.getFirstName();
String middleInitial = student.getMiddleInitial();
String lastName = student.getLastName();
String streetAddress = student.getStreetAddress();
....
StringBuilder errorBuilder = new StringBuilder();
//The predicate 'negate' method returns a predicate that represents the logical negation or opposite
if (isValidFirstName().negate().test(student)) {
String error = "Invalid FirstName: " + firstName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidMiddleInitial().negate().test(student)) {
String error = "Invalid Middle Initial: " + middleInitial;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidLastName().negate().test(student)) {
String error = "Invalid LastName: " + lastName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidStreetAddress().negate().test(student)) {
String error = "Invalid StreetAddress: " + streetAddress;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
...
if (errorBuilder.length() > 0) {
errorBuilder.deleteCharAt(errorBuilder.length() - 1);
}
return errorBuilder.toString().trim();
}
实际上,我对从 list.forEach()
调用的 getStudentErrorMessage()
感到困惑。我知道收藏家为您提供 Collectors.joining
功能。实际上我想以谓词的方式来做。就像我创建了所有谓词的列表,然后在流中使用它一样。我可以对我的错误消息做类似的事情吗?就像我从 invalid students List
创建流并使用 Collectors.toMap()
一样,将我的 Student 作为键并将错误消息作为其值。
谢谢
编辑 --------------
public class OlccStudentPredicate {
public static Predicate<OlccStudent> isValidTestDate() {
return p -> isValidDate(p.getTestDate());
}
public static Predicate<OlccStudent> isValidDateOfBirth() {
return p -> isValidDate(p.getDateOfBirth());
}
...
private static boolean isValidDate(String date) {
boolean valid = false;
if (StringUtils.isNotBlank(date)) {
try {
LocalDate.parse(date, DateTimeFormatter.ofPattern("MM/dd/yyyy"));
valid = true;
} catch (DateTimeParseException e) {
}
}
return valid;
}
@Test
public void test() {
List<OlccStudent> olccStudentsList = getOlccStudentsList();
try {
Map<OlccStudent, String> map = getInvalidStudentsMap(olccStudentsList);
System.out.println();
//olccStudentService.getStudentsMap(olccStudentsList);
} catch (Exception e) {
e.printStackTrace();
}
}
private List<OlccStudent> getOlccStudentsList() {
List<OlccStudent> olccStudentsList = new ArrayList<>();
OlccStudent student1 = new OlccStudent(1L, 1L, "firstName1", "middleInitial1", "lastName1", "email1",
"streetAdress1", "city1", "state1", "1234", "01/22/2015", "phoneNumber1", "01/22/2015",
"123456789", "testForm1", "providerCode1", "instructorCode1", "surveyAnswer1",
"testIdentifier1", "testAnswers1");
OlccStudent student2 = new OlccStudent(2L, 2L, "firstName2", "middleInitial2", "lastName2", "email2",
"streetAdress2", "city2", "state2", "5678", "02/22/2015", "phoneNumber2", "02/22/2015",
"987654321", "testForm2", "providerCode2", "instructorCode2", "surveyAnswer2",
"testIdentifier2", "testAnswers2");
OlccStudent student3 = new OlccStudent(3L,3L, "firstName3", "middleInitial3", "lastName3", "email3",
"streetAdress3", "city3", "state3", "zip3", "testDate3", "phoneNumber3", "dateOfBirth3",
"socialSecurityNumber3", "testForm3", "providerCode3", "instructorCode3", "surveyAnswer3",
"testIdentifier3", "testAnswers3");
OlccStudent student4 = new OlccStudent(4L, 4L, "firstName4", "middleInitial4", "lastName4", "email4",
"streetAdress4", "city4", "state4", "zip4", "testDate4", "phoneNumber4", "dateOfBirth4",
"socialSecurityNumber4", "testForm4", "providerCode4", "instructorCode4", "surveyAnswer4",
"testIdentifier4", "testAnswers4");
olccStudentsList.add(student1);
olccStudentsList.add(student2);
olccStudentsList.add(student3);
olccStudentsList.add(student4);
return olccStudentsList;
}
private String validate(OlccStudent student) {
String firstName = student.getFirstName();
String middleInitial = student.getMiddleInitial();
String lastName = student.getLastName();
String streetAddress = student.getStreetAddress();
String city = student.getCity();
String state = student.getState();
String zip = student.getZip();
String dateOfBirth = student.getDateOfBirth();
String ssn = student.getSocialSecurityNumber();
String phoneNumber = student.getPhoneNumber();
String testDate = student.getTestDate();
String testForm = student.getTestForm();
String testAnswers = student.getTestAnswers();
String providerCode = student.getProviderCode();
String instructorCode = student.getInstructorCode();
StringBuilder errorBuilder = new StringBuilder();
//The predicate 'negate' method returns a predicate that represents the logical negation or opposite
if (isValidFirstName().negate().test(student)) {
String error = "Invalid FirstName: " + firstName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidMiddleInitial().negate().test(student)) {
String error = "Invalid Middle Initial: " + middleInitial;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidLastName().negate().test(student)) {
String error = "Invalid LastName: " + lastName;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidStreetAddress().negate().test(student)) {
String error = "Invalid StreetAddress: " + streetAddress;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidCity().negate().test(student)) {
String error = "Invalid City: " + city;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidState().negate().test(student)) {
String error = "Invalid State: " + state;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidZip().negate().test(student)) {
String error = "Invalid Zip: " + zip;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidDateOfBirth().negate().test(student)) {
String error = "Invalid DateOfBirth: " + dateOfBirth;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidSsn().negate().test(student)) {
String error = "Invalid SSN: " + ssn;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidTestDate().negate().test(student)) {
String error = "Invalid TestDate: " + testDate;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidTestAnswers().negate().test(student)) {
String error = "Invalid TestAnswers: " + testAnswers;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidProviderCode().negate().test(student)) {
String error = "Invalid ProvideCode: " + providerCode;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (isValidInstructorCode().negate().test(student)) {
String error = "Invalid InstructorCode: " + instructorCode;
errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
}
if (errorBuilder.length() > 0) {
errorBuilder.deleteCharAt(errorBuilder.length() - 1);
}
return errorBuilder.toString().trim();
}
public Map<OlccStudent, String> getInvalidStudentsMap(List<OlccStudent> studentsList) throws Exception {
Map<OlccStudent, String> map = studentsList.stream()
// Step 1: Validate each student, keeping a track of any error message generated.
.collect(Collectors.toMap(Function.identity(), student -> validate(student)))
// Step 2: Keep only those that have an error message associated.
.entrySet()
.stream()
.filter(entry -> entry.getValue() != null)
// Step 3: Generate a Map.
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
return map;
}
第 1 部分:从 Collection
个对象生成 Map
如果我对你的问题的理解正确,你想从 Collection
个对象生成一个 Map
,这样 Map
中的值是在验证对象时生成的错误消息Collection
和键是验证失败的实际对象。
因此,如果您的输入 Collection
是:
Student 1: Valid
Student 2: Valid
Student 3: Invalid
Student 4: Valid
Student 5: Invalid
预期输出为:
Student 3: {error message}
Student 5: {error message}
如果这是你想要的,你的问题就太复杂了,可以通过假设存在一个函数来简化,例如:
/**
* Validates a student and returns an error message if the student data is
* not valid. The error message provides the actual reason why the student
* data is invalid.
*
* @param student The student to validate.
* @return An error message if {@code student} contains invalid data,
* {@code null} otherwise.
*/
String validate(OlccStudent student) { ... }
现在,任务很简单。
// Step 0: We have a collection of students as an input.
Collection<OlccStudent> students = ...;
students.stream()
// Step 1: Validate each student, keeping a track of any error message generated.
.collect(Collectors.toMap(Function.identity(), student -> validate(student)))
// Step 2: Keep only those that have an error message associated.
.entrySet()
.stream()
.filter(entry -> entry.getValue() != null);
// Step 3: Generate a Map.
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
第 2 部分:验证对象
最好使用 JSR303 - Java 验证 API(及其实现之一,例如 Hibernate Validator 或 Apache Beans Validator)来验证 beans。这不仅是标准化的,而且需要更少的努力和维护。添加新验证很容易,整个框架与区域设置无关,允许生成特定于区域设置的消息。
我会创建一个 ValidationRule
class 来将验证谓词和错误消息格式化程序结合在一起:
static class ValidationRule {
public final Predicate<OlccStudent> predicate;
public final Function<OlccStudent, String> errorFormatter;
public ValidationRule(Predicate<OlccStudent> predicate,
Function<OlccStudent, String> errorFormatter) {
this.predicate = predicate;
this.errorFormatter = errorFormatter;
}
}
现在 getAllRules
将如下所示:
public static List<ValidationRule> getAllRules() {
return Arrays.asList(
new ValidationRule(isValidFirstName(), s -> "Invalid FirstName: " + s.getFirstName()),
new ValidationRule(isValidMiddleInitial(), s -> "Invalid Middle Initial: " + s.getMiddleInitial()),
new ValidationRule(isValidLastName(), s -> "Invalid LastName: " + s.getLastName()),
new ValidationRule(isValidStreetAddress(), s -> "Invalid StreetAddress: " + s.getStreetAddress())
// ...
);
}
并且您可以通过以下方式获取无效学生地图:
public Map<OlccStudent, String> getInvalidStudentsMap(List<OlccStudent> students) {
List<ValidationRule> rules = getAllRules();
return students
.stream()
.<Entry<OlccStudent, String>>map(student -> new AbstractMap.SimpleEntry<>(student, rules
.stream()
.filter(rule -> rule.predicate.test(student))
.map(rule -> rule.errorFormatter.apply(student))
.collect(Collectors.joining(ERROR_MESSAGE_SEPERATOR))))
.filter(entry -> !entry.getValue().isEmpty())
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
}