【问题标题】:Selecting objects from an ArrayList based on value of certain fields根据某些字段的值从 ArrayList 中选择对象
【发布时间】:2015-10-10 00:21:16
【问题描述】:

我有一个 Course 类,它有各种类型的字段,以及它们各自的 getter 和 setter,例如:

float percentage
char courseLevel
String courseName
boolean takingNow

然后我有一个包含课程对象和方法的 ArrayList:

public <T> ArrayList<Course> getCourses(int parameterId, T value) {
    ArrayList<Course> res = new ArrayList<Course>();
    switch(parameterId) {
        case COURSE_NAME:
            for(Course c: courseList) {
                if(c.getCourseName().equals(value) {
                    res.add(c)
                }
            }
            break;
        ....
        //rest of the cases
        ....
    }
    return res
}

该方法然后遍历课程的ArrayList并根据parameterId比较某个字段,如果课程字段等于传递的值,则将其添加到要返回的arraylist中。

有没有比以这种方式使用泛型更好的方法来做到这一点,我觉得这是一种非常糟糕的编码习惯。

【问题讨论】:

  • 更好的方法是使用数据库。
  • 数据存储不是问题,检查的逻辑是问题。也就是说,您正在尝试对列表执行您应该对数据库执行的操作...
  • @AnubianNoob 我这样做的方式是将数据库中的所有课程加载到课程的 ArrayList 中。我不应该这样做吗?
  • “有没有比这样使用泛型更好的方法” 是的。如前所述,使用数据库。
  • @AndrewDelgadillo 您可以直接轮询数据库。或者有一个比 ArrayList 更好的包装器。

标签: java generics arraylist


【解决方案1】:

据我了解,您希望有一个方法可以返回所有符合指定条件的课程。

您可能会考虑研究 lambda... 一开始它们看起来有点时髦,但它们确实很强大,一旦您习惯了它就很容易了。

对于你的例子,你可能有这个:

import java.util.ArrayList;
import java.util.function.Predicate;

class Course {

    float percentage;
    char courseLevel;
    String courseName;
    boolean takingNow;

    public static ArrayList<Course> allCourses;

    public ArrayList<Course> getCourses(Predicate<Course> coursePredicate) {
        ArrayList<Course> toReturn = new ArrayList<>();
        for (Course c : allCourses
                            .stream()
                            .filter(coursePredicate)
                            .toArray(Course[]::new)) {
            toReturn.add(c);
        }
        return toReturn;
    }
}

这意味着我们可以这样称呼它:

getCourses(c -> c.courseLevel != 'B');

意思是所有没有'B'的课程级别将被退回...

这里发生了什么?好的。首先,我们获取一个集合,并将其转换为“Stream”。这允许我们使用Predicates(这只是匹配的条件)来过滤流。最后,我们可以将过滤后的Stream 转换回数组,然后将这些元素添加回ArrayList

lambda 定义了该条件。它只是一个接受Course 类型的单个参数的函数。在我的示例中,它相当于以下内容:

static boolean isMatch(Course c) {
    return c.courseLevel != 'B';
}

filter 方法循环遍历 Stream 中的所有元素,如果应用 Predicate(lambda,相当于上述方法)返回 true,则它包含在其结果中。

【讨论】:

  • 使用List接口和Collector简化了getCourses()方法:public List&lt;Course&gt; getCourses(Predicate&lt;Course&gt; coursePredicate) { return allCourses.stream().filter(coursePredicate).collect(Collectors.toList());}
【解决方案2】:

我不知道你是否使用 Java 8,但如果你是,你可以使用流并过滤你的 ArrayList。

语法可能如下所示:

return courseList.stream().filter(c -> c.getCourseName().equals(value));

您可以为 byCourseName、byCourseLevel 等设置一个单独的方法。主体是相同的。

如果您不使用 Java 8,您仍然可以使用单独的方法并避免使用 switch/case。

【讨论】:

  • 我对答案投了赞成票,但我仍然认为数据库是这里的最佳选择:)。因为有时我们可能不得不处理大量数据。
  • 我同意你的观点 100% ;)
  • 过滤器中的谓词并不总是c.getCourseName().equals(value)。实际谓词将取决于parameterId。 Java 8 在这里没有任何优势,除了我们可以将 switch/case 移动到代码中其他地方的谓词定义。
  • 我同意,但我也建议为每种类型使用单独的查找器方法,以减轻 parameterId 的差异以及对静态变量 COURSE_NAME 等的需求。
【解决方案3】:

如果您使用的是 java 8,则可以使用 Streams,尤其是 'filter' 。例如,请参阅本教程中的“过滤器”部分:http://zeroturnaround.com/rebellabs/java-8-explained-applying-lambdas-to-java-collections/

如果你只是按名称过滤,那么你可以选择一个 lambda 表达式“filter(p -> p.getName().equals(value))。但是因为你有一个更复杂的情况你最好调用专用方法。

【讨论】:

    猜你喜欢
    • 2021-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 1970-01-01
    • 2021-06-01
    相关资源
    最近更新 更多