Java 8 使用流从列表生成地图的优雅方式是什么

Java 8 使用流从列表生成地图的优雅方式是什么,java-8,java-stream,Java 8,Java Stream,我有我从数据库中得到的学生名单。我应用谓词列出列表,并用有效、无效的学生对列表进行分区。现在,对于无效的学生,我希望生成以学生为键、以错误消息为值的映射。因为我需要为每个学生生成报告。这里是我正在做的,但我不知道这是不是一个好方法,或者有更好的方法来做 实际上,在得到无效学生列表后,我正在尝试创建一个函数,但我认为代码变得混乱,可能有更好的方法来实现它。这就是我要做的 private List<Predicate<OlccStudent>> getAllPredicate

我有我从数据库中得到的学生名单。我应用谓词列出列表,并用有效、无效的学生对列表进行分区。现在,对于无效的学生,我希望生成以学生为键、以错误消息为值的映射。因为我需要为每个学生生成报告。这里是我正在做的,但我不知道这是不是一个好方法,或者有更好的方法来做

实际上,在得到无效学生列表后,我正在尝试创建一个函数,但我认为代码变得混乱,可能有更好的方法来实现它。这就是我要做的

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();
}
第1部分:从对象的集合生成映射
如果我正确理解您的问题,您希望从对象的
集合
生成
映射
,这样
映射
中的值是验证
集合
中的对象时生成的错误消息,键是未通过验证的实际对象

因此,如果您的输入
集合
为:

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()));
//步骤0:我们有一组学生作为输入。
收集学生=。。。;
学生们。stream()
//步骤1:验证每个学生,跟踪生成的任何错误消息。
.collect(Collectors.toMap(Function.identity(),student->validate(student)))
//步骤2:只保留那些与错误消息相关联的。
.entrySet()
.stream()
.filter(entry->entry.getValue()!=null);
//步骤3:生成地图。
.collect(Collectors.toMap(entry->entry.getKey(),entry->entry.getValue());
第2部分:验证对象

最好使用JSR303-Java验证API(及其实现之一,如Hibernate Validator或ApacheBeans Validator)来验证bean。这不仅是标准化的,而且需要更少的工作和维护。添加新的验证很容易,整个框架与语言环境无关,允许生成特定于语言环境的消息。

我将创建一个
ValidationRule
类,将验证谓词和错误消息格式化程序粘在一起:

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;
    }
}
您可以通过以下方式获得无效学生的地图:

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()));
}
公共地图getInvalidStudentsMap(列出学生){
列表规则=getAllRules();
留学生
.stream()
.map(学生->新建抽象地图.SimpleEntry(学生,规则
.stream()
.filter(规则->规则.predicate.test(学生))
.map(规则->规则.errorFormatter.apply(学生))
.collect(收集器.joining(错误消息分隔符)))
.filter(条目->!entry.getValue().isEmpty())
.collect(Collectors.toMap(entry->entry.getKey(),entry->entry.getValue());
}

谢谢您的回答。您的评论非常有用,但我已经将我的列表划分为有效和无效。实际上,我首先进行分区,因为我必须只为无效学生创建地图。有效的学生不需要地图。我尝试了这个
Map invalidStudentsMap=invalidStudentsList.stream().collect(Collectors.toMap(p->p,(p)->getStudentErrorMessage(p))我的示例的最终结果是一个只包含无效学生的
映射。你试过了吗?如果我在你的代码中使用getStudentErrorMessage(p)。在您的代码中是validate(学生)。所以没关系。然后我得到所有4条记录,其中2条有效记录没有值,两条无效记录有错误值。我已经运行了我的代码,它的工作原理与我描述的完全相同。首先,逐字记录并运行它,以确保它做的是广告。然后,如果您想将
validate
重命名为
getstudenterromessage
,这很好,因为这只是一个方法的重命名。我认为您坚持使用原始代码,试图插入我提供的部分示例,并得出结论认为它不起作用。否则,请在Gist上发布一个示例,其中完全包含演示任何问题的代码。我提供给你的东西是从我自己的生产应用程序中获取的,所以我知道它在现实生活中也能工作。我编辑了我的帖子,添加了你代码的测试样本。请查收。我在地图上有4个实体。我应该得到2。学生1和2有效,因为它们包含有效日期,而3和4无效,因为它们包含无效的日期格式。Thanks@Tiger瓦列夫,谢谢。我尝试了您的代码,但得到的错误是
类型不匹配:无法从Map转换为Map
@Basit:可能您的编译器版本较旧,无法推断类型参数。我在
map
中添加了显式参数,请立即尝试。我同意这一建议,因为它使代码更面向对象,并允许将来更轻松地添加更多规则。在链式调用中,还有一个使用匿名类型的技巧<代码>java students.stream().map(student->new Object(){OlccStudent=student;String rule=rules;}).filter(x->!x.rule.isEmpty())/@TagirValeev是的,绝对是的,这对我来说真的很有用。所以,我称之为诡计。在这种情况下,
映射(…)
的泛型参数
不仅是一个
对象
,而且是一个具体的匿名类型(例如
验证规则$0
)。因此,您可以在继续调用中访问匿名类型的成员
规则
,但不能将其分配给任何变量。因为不能将具体类型声明为变量类型,所以只能将变量声明为
对象
,这样就不能再访问成员。
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()));
}