Home > database >  Unable to get my head around Predicate isEqual method
Unable to get my head around Predicate isEqual method

Time:06-02

In Java docs it is given -

Modifier and Type                      Method and Description
static <T> Predicate<T>                isEqual(Object targetRef)
                                       Returns a predicate that tests if two arguments are equal according to Objects.equals(Object, Object).

In https://www.geeksforgeeks.org/java-8-predicate-with-examples/

it is given -

isEqual(Object targetRef) : Returns a predicate that tests if two arguments are equal according to Objects.equals(Object, Object).

static Predicate isEqual(Object targetRef) Returns a predicate that tests if two arguments are equal according to Objects.equals(Object, Object). T : the type of arguments to the predicate Parameters: targetRef : the object reference with which to compare for equality, which may be null Returns: a predicate that tests if two arguments are equal according to Objects.equals(Object, Object)

I can't get a grisp of what this Objects.equals(Object, Object) might be

I write the following code to try it out -

Class Fruits -

Fruits.java -

public class Fruits {
    private String fruit;

    public Fruits(String fruit) {
        this.fruit = fruit;
    }

    public String getFruit() {
        return fruit;
    }
}

Here, the other methods of predicate seem to be quite easy to understand -

Predicate<List<Fruits>> containsApple = list -> {
            boolean myReturn = false;
            Iterator<Fruits> iterator = list.iterator();
            while (iterator.hasNext()) {
                Fruits fruits = iterator.next();
                String fruit = fruits.getFruit();
                if (fruit.equals("Apple")) {
                    myReturn = true;
                    break;
                }
            }
            return myReturn;
        };
Predicate<List<Fruits>> containsOrange = list -> {
            boolean myReturn = false;
            Iterator<Fruits> iterator = list.iterator();
            while (iterator.hasNext()) {
                Fruits fruits = iterator.next();
                String fruit = fruits.getFruit();
                if (fruit.equals("Orange")) {
                    myReturn = true;
                    break;
                }
            }
            return myReturn;
        };
Predicate<List<Fruits>> containsAppleAndOrange = list -> {
            return containsApple.and(containsOrange).test(list);
        };
Predicate<List<Fruits>> containsAppleOrRange = list -> {
            return containsApple.or(containsOrange).test(list);
        };

Predicate<List<Fruits>> notContainsApple = list -> {
            return containsApple.negate().test(list);
        };
Predicate<List<Fruits>> notContainsOrange = list -> {
            return containsOrange.negate().test(list);
        };
Predicate<List<Fruits>> notContainsAppleAndOrange = list -> {
            return containsAppleAndOrange.negate().test(list);
        };
Predicate<List<Fruits>> notContainsAppleOrOrange = list -> {
            return containsAppleOrRange.negate().test(list);
        };

Here I test it with following data -

        List<Fruits> list1 = new ArrayList<>(List.of(
                new Fruits("Apple"),
                new Fruits("Orange"),
                new Fruits("Mango"),
                new Fruits("Banana")
        ));

        List<Fruits> list2 = new ArrayList<>(List.of(
                new Fruits("Apple"),
                new Fruits("Mango"),
                new Fruits("Banana"),
                new Fruits("Berry")
        ));

        List<Fruits> list3 = new ArrayList<>(List.of(
                new Fruits("Orange"),
                new Fruits("Mango"),
                new Fruits("Banana"),
                new Fruits("Berry")
        ));

Result is as expected.

But in no way can I understand how to implement the isEqual() method -

To see that two arguments are equal are not I create another predicate -

redicate<List<Fruits>> containsApple2 = list -> {
            boolean myReturn = false;
            Iterator<Fruits> iterator = list.iterator();
            while (iterator.hasNext()) {
                Fruits fruits = iterator.next();
                String fruit = fruits.getFruit();
                if (fruit.equals("Apple")) {
                    myReturn = true;
                    break;
                }
            }
            return myReturn;
        };

I try something like (without understanding why) -

System.out.println(Predicate.isEqual(containsApple).test(list1));

Output - false

Now what happened here?

System.out.println(Predicate.isEqual(containsApple2).test(containsApple));

Output - false

Now again what happened here?

So, how to exactly use this isEqual method?

CodePudding user response:

Predicate.isEqual is a factory method that creates predicates that test if a given thing is equal to the parameter passed in.

Predicate.isEqual(containsApple) creates a Predicate<Predicate<List<Fruits>>> that tests if a given thing is equal to containsApple. However, since containsApple refers to an instance created from a lambda, and nothing much is guaranteed about the equality of instances created from lambda expressions (See the JLS), nothing much can be said about the result of calling test on it. The classes of the lambda instances may or may not implement equals, and containsApple may or may not be the same instance as containsApple2, depending on the implementation.

Rather than comparing lambda instances, a typical example of using Predicate.isEqual is:

Fruits apple = new Fruits("Apple");
Predicate<Fruits> isApple = Predicate.isEqual(apple);
// rather than this slightly longer version:
// Predicate<Fruits> isApple = x -> Objects.equals(x, apple);

Then you can pass isApple around, to other methods that take Predicates, and/or call test on it. isApple.test(apple) would be true, isApple.test(new Fruits("something else")) would be false. I would also recommend that you override equals and hashCode in Fruits.

Note that we generally make predicates that test against individual objects, rather than lists (collections) of things. We would pass these predicates to other methods (such as Stream.filter), and let them do the filtering. For example, to filter a list to get all the apples:

List<Fruits> apples = fruitsList.stream()
    .filter(Predicate.isEqual(apple)).toList();

CodePudding user response:

One should use singular here for the class Fruits.

First you must establish equality of Fruit. Also should you ever want it to store in a HashMap or HashSet, a hashCode implementation is important.

public class Fruit {
    private final String fruit; // Or name.

    public Fruit(String fruit) {
        this.fruit = fruit;
    }

    public String getFruit() {
        return fruit;
    }

    @Override
    public boolean equals(Object other) {
        return other instanceOf Fruit && ((Fruit) other).fruit.equals(fruit);
    }

    @Override
    public int hashCode() {
        return fruit.hashCode();
    }
}

The Iterator class is rather old and its primary advantage is you can walk through and still remove an element with iterator.remove(), which is not allowed on the List in a - statefull - for (ConcurrentModificationException).

Predicate<List<Fruit>> containsApple = list -> {
    for (Fruit fruit: list) {
        if (fruit.getFruit().equals("Apple")) {
            return true;
        }
    }
    return false;
};

Predicate<List<Fruit>> containsApple = list -> list.contains(new Fruit("Apple"));

Advisable is to get acquainted with Stream (like for iterating through a collection) and its expressive power.

Predicate<List<Fruit>> containsApple = list ->
    list.stream()
        .anyMatch(fr -> fr.getFruit().equals("Apple"));
  • Related