11 Replies - 775 Views - Last Post: 12 May 2019 - 12:49 AM Rate Topic: -----

#1 overwhelmed_student   User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 63
  • Joined: 10-May 18

Generic list filter

Posted 09 May 2019 - 05:46 AM

Hey guys, I have a question I am very much interested in.
I was taking generics one step further and came across filter(). I was wondering:
If I have a generic interface predicate and I want to use this interface to get a filter on lists with that method:

public interface ListPredicate<T> {

    boolean fulfillsRequirements(T objectParameter);
}


When I write another class to implement this test-method in order to filter if the object complies with the requirements or not, what would it look like? Let's use String as an example.
Would the second program be
public class ListFilter<String> implements ListPredicate


or
public class ListFilter<T> implements ListPredicate


This post has been edited by overwhelmed_student: 09 May 2019 - 05:47 AM


Is This A Good Question/Topic? 0
  • +

Replies To: Generic list filter

#2 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7429
  • View blog
  • Posts: 15,403
  • Joined: 16-October 07

Re: Generic list filter

Posted 10 May 2019 - 03:52 AM

In each case, you'd need to include the type:
class ListFilter<String> implements ListPredicate<String> {
    @Override
    public boolean fulfillsRequirements(String objectParameter) {
// or
class ListFilter<T> implements ListPredicate<T> {
    @Override
    public boolean fulfillsRequirements(T objectParameter) {



However, for a filter... every java class implements equals, so if the objects in a collection implement that properly, you're fine.

Alternately, if you want to specify some particular rules for a filter, you could pass your own predicate. More here: https://howtodoinjav...cate-in-java-8/
Was This Post Helpful? 3
  • +
  • -

#3 overwhelmed_student   User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 63
  • Joined: 10-May 18

Re: Generic list filter

Posted 10 May 2019 - 07:37 AM

View Postbaavgai, on 10 May 2019 - 03:52 AM, said:

In each case, you'd need to include the type

Thanks!
About the filter: I was wondering if it is possible to implement a generic filter? But I wouldn't know how to use it. If I have a generic List<T> class that I specifically implemented myself, it can be filled with any objects, right?
So I could use it for String, Integer, or something like Animal() that I implemented myself.
How would I implement a filter that returns a list with only mammals? T objectParameter is generic which is why I can't access the methods or variables of the Animal class. How would I implement a filter knowing that the list could be a simple List<String> but also List<Animal>?
While I don't know even one case where this is needed, it would help me understand more about generics.
Was This Post Helpful? 0
  • +
  • -

#4 overwhelmed_student   User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 63
  • Joined: 10-May 18

Re: Generic list filter

Posted 10 May 2019 - 09:30 AM

I want to ask my question in a different way because I feel like it is quite aconfusing. I implemented a ListPredicate with boolean test(Object parameter).
Now I want to filter all animals from this list, that have at least a certain height and if they are mammals (or not):
public class AnimalListFilter implements ListPredicate<Animal> {
    boolean isMammal;
    int height;

    AnimalListFilter(boolean isMammal, int height) {
        this.isMammal = isMammal;
        this.height = height;
    }

    public boolean test (Animal animal) {
        if(animal.isMammal == isMammal && animal.height >= height) {
            return true;
        }
        else {
            return false;
        }
    }
}


First question: In order to get the conditions, I added a constructor that sets the conditions as instance variables. Is there any better way to do it? Is the constructor inconvenient? (I know about lambda-expression but I want to learn it without lambdas.)
Second question: How could I make it generic? Is it even possible? How can I implement this test with a generic parameter?

This post has been edited by overwhelmed_student: 10 May 2019 - 09:58 AM

Was This Post Helpful? 0
  • +
  • -

#5 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7429
  • View blog
  • Posts: 15,403
  • Joined: 16-October 07

Re: Generic list filter

Posted 10 May 2019 - 11:14 AM

The code looks fine. Do you have some functioning code where you're using this?

I'd want Animal to implement something like:
interface Animal {
    bool isMammal();
    int getHeight();
}



I would add that return true; is kind of redundant. You can reasonably do this:
public class AnimalListFilter implements ListPredicate<Animal> {
    private final boolean isMammal;
    private final int height;

    public AnimalListFilter(boolean isMammal, int height) {
        this.isMammal = isMammal;
        this.height = height;
    }

    public boolean test (Animal animal) {
        return animal.isMammal() == isMammal && animal.getHeight() >= height;
    }
}


Was This Post Helpful? 1
  • +
  • -

#6 overwhelmed_student   User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 63
  • Joined: 10-May 18

Re: Generic list filter

Posted 11 May 2019 - 08:29 AM

View Postbaavgai, on 10 May 2019 - 11:14 AM, said:

The code looks fine.

I would add that return true; is kind of redundant.

You're right, that looks better! But how wouuld I make it generic? Is it possible to do it like this?
public class AnimalListFilter<T> implements ListPredicate<T> {

    public AnimalListFilter(T objectFiltered) {}

    public boolean test (T object) {
        return object.equals(objectFiltered);
    }
}


However, I already see one problem with it: If we keep Animal like this and I want to filter only mammals, I wouldn't want to specify the height. So I wouldn't be able to use the constructor with height and mammal. Is it possible to implement this somehow?
Like, when I add the instance variable "int count;" to Animal which tells me how many animals with that height exist. How would I write a generic filter where I print the count of animals over 50cm regardless of the boolean isMammal?
Was This Post Helpful? 0
  • +
  • -

#7 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7429
  • View blog
  • Posts: 15,403
  • Joined: 16-October 07

Re: Generic list filter

Posted 11 May 2019 - 09:55 AM

View Postoverwhelmed_student, on 11 May 2019 - 10:29 AM, said:

Is it possible to do it like this?

Yep, all objects support equals.

View Postoverwhelmed_student, on 11 May 2019 - 10:29 AM, said:

I want to filter only mammals, I wouldn't want to specify the height.

Before we get going, you really should read up on the modern way to do this, streams and lambdas: https://howtodoinjav...cate-in-java-8/

If you want some kind of test, then look at Comparable first: https://docs.oracle....Comparable.html

Yes, if you want to change the rules, you change the object. Or, you could make a more clever object... Something like:
public class AnimalListFilter implements ListPredicate<Animal> {
    // do only this test
    public AnimalListFilter(boolean isMammal) { /* your code */ }

    // do only this test
    public AnimalListFilter(int height) { /* your code */ }

    // do both tests
    public AnimalListFilter(boolean isMammal, int height) { /* your code */ }



Note, because of the interesting way Java instantiates interfaces, you can actually do something like:
interface ListPredicate<T> {
    boolean test (T e);
}

ListPredicate<Animal> filter1 = new ListPredicate<Animal>() {
    public boolean test (Animal animal) {
        return animal.isMammal() == isMammal && animal.getHeight() >= 10;
    }
};

ListPredicate<Animal> filter2 = new ListPredicate<Animal>() {
    public boolean test (Animal animal) { return animal.getHeight() >= 15; }
};


Was This Post Helpful? 1
  • +
  • -

#8 overwhelmed_student   User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 63
  • Joined: 10-May 18

Re: Generic list filter

Posted 11 May 2019 - 10:16 AM

View Postbaavgai, on 11 May 2019 - 09:55 AM, said:

Yes, if you want to change the rules, you change the object. Or, you could make a more clever object...

This is super helpful!
But I can't do something like
public class ListFilter<T> implements ListPredicate<T> 


because I would have to specify beforehand of which class this object is, right? I can't write a test for an object if I don't know yet what it is?

What if I create a generic list and want to use a predicate to filter it?
public class GenericList<T> implements Iterable<T> {
    private final List<T> genericList;

    public GenericList(List<T> genericList) {
        this.genericList = genericList;
    }

    public GenericList filter(GenericList<T> genericList, ListFilter<T> listFilter) {
        List<T> newList = new ArrayList<>();

        for(int i = 0; i < list.size(); i++) {
            //does the list filter even work when it is still generic 
            if(listFilter.test(list.get(i)) == true) {
                newList.add(list.get(i));
            }
        }
        GenericList newGenericList = new GenericList(newList);
        return newGenericList;
    }


To me that seems impossible right now. But a lot of stuff seemed impossible before that because I just couldn't solve it. That is why I am asking.
Was This Post Helpful? 0
  • +
  • -

#9 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7429
  • View blog
  • Posts: 15,403
  • Joined: 16-October 07

Re: Generic list filter

Posted 11 May 2019 - 11:20 AM

Yep, that works!

To be clear, while your definition T is the generic, the concrete is implemented. Meaning:
// this
public GenericList<T> filter(ListFilter<T> listFilter) {
// would be
public GenericList<Animal> filter(ListFilter<Animal> listFilter) {



Understand? You're not passing Function<T,bool>. When you're writing code you're passing something like Function<Animal,bool> or Function<String,bool> or whatever. The generic type place holder is just a place holder. Code that ultimately does something replaces that place holder with something more concrete.

Playing around in abstraction land will only grant you so much insight. Try writing code that does something you want to do. Create a list of Person. Filter my gender, age, politics, favorite pet, whatever.

Generics, and abstractions in general, are something a beginning programmer should do after they have written something useful. Did you get that sort to work with ints? Can you make it work with strings? Can you then make a generic so you can reuse the code for both?
Was This Post Helpful? 1
  • +
  • -

#10 overwhelmed_student   User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 63
  • Joined: 10-May 18

Re: Generic list filter

Posted 11 May 2019 - 11:52 AM

View Postbaavgai, on 11 May 2019 - 11:20 AM, said:

Generics, and abstractions in general, are something a beginning programmer should do after they have written something useful. Did you get that sort to work with ints? Can you make it work with strings? Can you then make a generic so you can reuse the code for both?


I did write a class
public class AnimalList()


with everything a regular list can do, too, but also the filter-method. The filter looks like that:
public class AnimalListFilter implements AnimalPredicate<Animal> {

     public boolean test (Animal animal) {
	    return animal.isMammal == true
     }


to filter all animals that are mammals. I tried to take it a step further to make it generic. But I can't just do
public class ListFilter<T> implements ListPredicate<T> {

     public boolean test(T objectParameter) {
          return objectParameter.isMammal == true
     }
}


because objectParameter has no instance variables. This is what I am trying to understand. I will use this generic method on an AnimalList with animals() in the end to see, if I get the same result. But I only get errors. And if the list would be filled with Strings or People or Dates, I couldn't filter it for mammals. It obviously doesn't work for everything. So how would I write a test like above completely generic? And still get my mammals?

This post has been edited by overwhelmed_student: 11 May 2019 - 11:55 AM

Was This Post Helpful? 0
  • +
  • -

#11 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7429
  • View blog
  • Posts: 15,403
  • Joined: 16-October 07

Re: Generic list filter

Posted 11 May 2019 - 02:40 PM

Ok, sure, let's play.

A class to play with:
class Person {
    public final String name;
    public final boolean male;
    public final int age;
    public Person(String name, int age, boolean male) {
        this.name = name;
        this.age = age;
        this.male = male;
    }
    public String toString() { return name + " " + age + " " + (male?"M":"F"); }
}

class People {
    private final List<Person> items = new ArrayList<Person>();
    public People add(Person p) { items.add(p); return this; }
    public People add(String name, int age, boolean male) { return add(new Person(name, age, male)); }
    public String toString() {
        String result = "";
        for(Person x : items) {
            result += (result == "" ? "" : ", ") + x;
        }
        return result;
    }
    public String toString(boolean male) {
        String result = "";
        for(Person x : items) {
            if (x.male != male) { continue; }
            result += (result == "" ? "" : ", ") + x;
        }
        return result;
    }
}



And some test code:
public static void test() {
    People xs = new People()
        .add("Alice", 24, false)
        .add("Bob", 54, true)
        .add("Chuck", 36, true)
        .add("Deb", 42, false);
    System.out.println(xs);
    System.out.println(xs.toString(true));
}



Now, can we generalize our filter? How about:
interface Filter<T> {
    boolean test(T e);
}

class People {
    private final List<Person> items = new ArrayList<Person>();
    public People add(Person p) { items.add(p); return this; }
    public People add(String name, int age, boolean male) { return add(new Person(name, age, male)); }

    public String toString(Filter<Person> filter) {
        String result = "";
        for(Person x : items) {
            if (!filter.test(x)) { continue; }
            result += (result == "" ? "" : ", ") + x;
        }
        return result;
    }
    public String toString(boolean male) {
        return toString(new Filter<Person>() { 
            public boolean test(Person e) { return e.male == male; }
        });
    }
    
    public String toString() {
        return toString(new Filter<Person>() { 
            public boolean test(Person e) { return true; }
        });
    }
}



Hmm... now, can we derive a generic collection from that? Perhaps:
class FilterList<T> {
    private final List<T> items = new ArrayList<T>();
    public FilterList add(T e) { items.add(e); return this; }

    public String toString(Filter<T> filter) {
        String result = "";
        for(T x : items) {
            if (!filter.test(x)) { continue; }
            result += (result == "" ? "" : ", ") + x;
        }
        return result;
    }
    
    public String toString() {
        return toString(new Filter<T>() { 
            public boolean test(T e) { return true; }
        });
    }
}

class People extends FilterList<Person> {
    public People add(String name, int age, boolean male) { add(new Person(name, age, male)); return this; }

    public String toString(boolean male) {
        return toString(new Filter<Person>() { 
            public boolean test(Person e) { return e.male == male; }
        });
    }
}



Hope this helps.
Was This Post Helpful? 2
  • +
  • -

#12 overwhelmed_student   User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 63
  • Joined: 10-May 18

Re: Generic list filter

Posted 12 May 2019 - 12:49 AM

View Postbaavgai, on 11 May 2019 - 02:40 PM, said:

Hope this helps.


It helped a lot, thanks! I didn't know that you could just implement the test in the class! My program works on a generic level now!
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1