4

I have a Course class, which has fields of various types, with their respective getters and setters, such as:

float percentage
char courseLevel
String courseName
boolean takingNow

I then have an ArrayList of course objects and a method:

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
}

the method then goes through the ArrayList of courses and compares a certain field based on the parameterId and if the course field equals the passed value it adds it to the to be returned arraylist.

Is there a better way to do this than using generics in this way, which I feel is a very poor coding practice.

6
  • 3
    Better way would be to use a database. Commented Jul 20, 2015 at 18:40
  • Data storage isn't the problem, the logic to check is the problem. That said, you are trying to do to a List what you should be trying to do to a database... Commented Jul 20, 2015 at 18:41
  • @AnubianNoob the way I did it was i loaded all of the courses from a database into an ArrayList of courses. Should I not do it this way? Commented Jul 20, 2015 at 18:45
  • "Is there a better way to do this than using generics in this way" Yes. As already mentionned, use a database. Commented Jul 20, 2015 at 18:45
  • 1
    @AndrewDelgadillo You could poll the database directly. Or have a better wrapper around it than an ArrayList. Commented Jul 20, 2015 at 18:46

3 Answers 3

5

As far as I understand it, you want to have a method that returns all courses that match a specified condition.

You might consider looking into lambdas... they look a little funky at first, but they're really powerful and once you get used to it it's pretty easy.

For your example, you might have this:

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;
    }
}

This means we can call it like so:

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

meaning all that don't have a course level of 'B' will be returned...

What's going on here? OK. First, we take a collection, and turn it into a "Stream". This allows us to use Predicates (which are just conditions to match on) to filter the stream. Finally, we can convert the filtered Stream back to an array, and then add those elements back into an ArrayList.

The lambda defines that condition. It is just a function that accepts a single argument of type Course. In my example, it would be equivalent to the following:

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

The filter method loops over all the elements in the Stream and if applying the Predicate (the lambda, equiv to the above method) returns true, it's included in its result.

Sign up to request clarification or add additional context in comments.

1 Comment

Using the List interface and using a Collector simplifies the getCourses() method: public List<Course> getCourses(Predicate<Course> coursePredicate) { return allCourses.stream().filter(coursePredicate).collect(Collectors.toList());}
2

I don't know if you're using Java 8 or not, but if you are, you could use streams and filter your ArrayList.

The syntax might look like:

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

You could have a separate method for byCourseName, byCourseLevel, etc. The principal would be the same.

If you're not using Java 8, you could still use separate methods and avoid the need for the switch/case.

4 Comments

I up voted the answer but still I think the database is the best option here :). Because sometimes we may have to deal with a large amount of data.
I agree with you 100% ;)
The predicate in the filter is not always c.getCourseName().equals(value). The actual predicate will depend on the parameterId. Java 8 doesn't give any advantage here, other than that we can move the switch/case to the predicate definition elsewhere in the code.
I agree, but I also suggested having a separate finder method for each type which mitigates the variance in parameterId and also the need for the static variable COURSE_NAME, etc.
0

If you're using java 8, you can use Streams and in particular 'filter' . see for example the 'Filter' section in this tutorial: http://zeroturnaround.com/rebellabs/java-8-explained-applying-lambdas-to-java-collections/

If you were only filtering by name, then you can settle for a lambda expression "filter(p -> p.getName().equals(value)). But since you have a more complex case you're better off invoking a dedicated method.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.