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?
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();
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"));
As mentioned by #user16320675 in comments one of the simplest examples would be -
import java.util.function.Predicate;
public class App {
public static void main(String[] args) {
Integer num1 = 2;
Integer num2 = 3;
Predicate<Integer> predicate = Predicate.isEqual(num1);
System.out.println(predicate.test(num1));
System.out.println(predicate.test(num2));
}
}
Output -
true
false
The code can also be rewritten as -
System.out.println(Predicate.isEqual(num1).test(num1));
System.out.println(Predicate.isEqual(num1).test(num2));
with same output.
A practical application in Java streams -
Code -
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class App {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Elephant");
list.add("Hippo");
list.add("Rhino");
list.add("Deer");
list.add("Hippo");
list.add("Zebra");
Predicate<String> predicate = Predicate.isEqual("Hippo");
list.stream().filter(predicate).forEach(System.out::println);
}
}
Output -
Hippo
Hippo
I want to get the next element from a spliterator, not just "perform action" on the next element. For example by implementing the following method
<T> T getnext(Spliterator<T> s) {
}
All search results I found just said that tryAdvance() was like a combination of an iterators hasNext() and next(), except that is a BIG LIE because I can't get next element, just "perform action on next element".
You can wrap the item in a list and then return from that list:
public static <T> T getNext(Spliterator<T> spliterator) {
List<T> result = new ArrayList<>(1);
if (spliterator.tryAdvance(result::add)) {
return result.get(0);
} else {
return null;
}
}
To make it more obvious to the caller that this is an operation that may return null, consider returning Optional:
public static <T> Optional<T> getNext(Spliterator<T> spliterator) {
final List<T> result = new ArrayList<>(1);
if (spliterator.tryAdvance(result::add)) {
return Optional.of(result.get(0));
} else {
return Optional.empty();
}
}
transform spliterator to stream
<T> Optional<T> getnext(Spliterator<T> s) {
return StreamSupport.stream(s, false).findFirst();
}
example
List<String> lst = List.of("a", "b");
Spliterator<String> spliterator = lst.spliterator();
System.out.println(getnext(spliterator)); //Optional[a]
System.out.println(getnext(spliterator)); //Optional[b]
There are no direct way but you can use :
<T> T getnext(Spliterator<T> s) {
Stream<T> stream = StreamSupport.stream(s, false); // convert Spliterator to stream
Iterable<T> iterable = stream::iterator; // then stream to Iterable
return iterable.iterator().next(); // then you can get next element
}
Or in one step like so :
return StreamSupport.stream(s, false).iterator().next();
Or more simpler :
return Spliterators.iterator(s).next();
I want to pass two lambda expressions (or something similar, I'm still getting familiar with all the terminology) into a method; the first one will get a list of items, and the second one will retrieve one Integer object from (each one of) those items.
So I want to have a method something like this:
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Integer>> listGetter,
Supplier<Integer> idGetter)
{
List<Object> objectList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Object o: objectList)
{
idList.add(idGetter.get());
}
return idList;
}
and call it something like this:
List<Integer> idList = setRecentList(searchParams,
(a) -> serviceOne.getItemList(a),
Item::getItemId);
So the first function is one called on an instance variable that the called method will have access to, and the second function is an instance method on any one of the objects returned as a list by the first function.
But the (eclipse) compiler doesn't like Item::getItemId, with or without parentheses at the end.
Do I just have a syntax thing wrong, or is there something else wrong with this idea?
Edit after many helpful comments -- thanks to you all!
I have one problem left. I've now got a method that I think does what I want, but I'm not sure how to pass the second expression to call it. Here is the method:
private List<Integer> getRecentList(List<Integer> salesCodeIdList,
Function<List<Integer>, List> listGetter,
Function<Object, Integer> idGetter
) {
List<Object> objectList = listGetter.apply(salesCodeIdList);
List<Integer> idList = new ArrayList<>();
for (Object o : objectList) {
idList.add(idGetter.apply((o)));
}
return idList;
}
AFAIK, I have to leave the second List raw in the getter spec, since different getters will return different types of objects in their lists.
But I still don't know how to invoke it -- I want to pass a method that gets an id from a particular instance of object, i.e., I want to pass a getter on the ID of one of the objects returned by the listGetter. That will be different types of objects on different calls. How would I invoke that?
To go back to examples, if I had a Supplier class with getSupplierId(), and a Vendor class with getVendorId(), and I cannot change those classes, can I pass in the correct method to invoke on the objects in the list depending on which getter retrieved the list?
A likely reason why you get the error is hidden inside the implementation of your method:
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Object>> listGetter,
Supplier<Integer> idGetter)
{
List<Object> objectList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Object o: objectList)
{
idList.add(idGetter.get()); // <<<<<==== Here
}
return idList;
}
Note how o from the for loop is not used in the call, indicating that idGetter would get you an ID out of thin air. That's of course is not true: you need to pass an item to idGetter, which means that the call should be
idList.add(idGetter.apply(o));
(from the comment) I can't cast within the method, I don't know what type of object to cast to there.
which in turn means that idGetter should be Function<Object,Integer>:
for (Object o: objectList)
{
idList.add(idGetter.apply(o));
}
Since you would like to reuse the same function for lists of different types, the call would have to use a lambda that performs the cast at the caller, i.e. at the point where you know the type:
List<Integer> idList = setRecentList(searchParams,
(a) -> serviceOne.getItemList(a),
(o) -> ((Item)o).getItemId());
Item::getItemId is not a Supplier<Integer>. A Supplier<Integer> is a lambda function that takes no arguments and returns an Integer. But Item::getItemId would require an Item to get the ID from.
Here is probably what you want. Note I've changed your method signature to match the idea that you're getting IDs from items.
class Item {
int id;
Item (int i) {
id = i;
}
int getItemId() {
return id;
}
}
private static List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Item>> listGetter,
Function<Item, Integer> idGetter)
{
List<Item> itemList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Item item: itemList)
{
idList.add(idGetter.apply(item));
}
return idList;
}
Edit: If you want this to handle multiple kinds of Items, and they all have IDs, you can do something like the following. First, define an interface to represent the fact that your different kinds of Items all have IDs:
interface ItemWithId {
abstract int getItemId();
}
class ItemA implements ItemWithId {
int id;
ItemA(int i) {
id = i;
}
public int getItemId() {
return id;
}
}
class ItemB implements ItemWithId {
int id;
ItemB(int i) {
id = i;
}
public int getItemId() {
return id;
}
}
Then, your method should use ItemWithId instead of Item:
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<ItemWithId>> listGetter,
Function<ItemWithId, Integer> idGetter)
{
List<ItemWithId> itemList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (ItemWithId item: itemList)
{
idList.add(idGetter.apply(item));
}
return idList;
}
Finally, you can call this by casting an ItemA or ItemB to an ItemWithId:
List<Integer> idList = getRecentList(searchParams,
(a) -> getItemList(a),
(item) -> ((ItemWithId) item).getItemId());
Another edit: Ok, so you can't change ItemA or ItemB. You can still make it work:
class ItemA {
int id;
ItemA(int i) {
id = i;
}
public int getItemIdA() {
return id;
}
}
class ItemB {
int id;
ItemB(int i) {
id = i;
}
public int getItemIdB() {
return id;
}
}
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Object>> listGetter,
Function<Object, Integer> idGetter)
{
List<Object> itemList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Object item: itemList)
{
idList.add(idGetter.apply(item));
}
return idList;
}
List<Integer> idList = getRecentList(searchParams,
(a) -> getItemList(a),
(item) -> ((ItemA) item).getItemIdA());
// or
List<Integer> idList = getRecentList(searchParams,
(a) -> getItemList(a),
(item) -> ((ItemB) item).getItemIdB());
I have a list of objects as below,
Emp e1 = new Emp(10,"Anitha",1000,"AE");
Emp e2 = new Emp(20,"Chaitanya",2000,"SE");
Emp e3 = new Emp(30,"Chaitanya",3000,"SE");
Emp e4 = new Emp(40,"Deepthi",2100,"AE");
Emp e5 = new Emp(50,"Deepthi",2200,"CE");
Emp e6 = new Emp(60,"Deepthi",2300,"BE");
Emp e7 = new Emp(70,"Anitha",2300,"BE");
Emp e8 = new Emp(80,"Anitha",2400,"ME");
Emp e9 = new Emp(90,"Sita",2200,"CE");
Emp e10 = new Emp(100,"Hari",2200,"CE");
Emp e11 = new Emp(110,"Krishna",2200,"CE");
I would like to filter the values on unique name and also filter on same name like
1.on unique name : output should be
(50,"Deepthi",2200,"CE")
(100,"Hari",2200,"CE")
(110,"Krishna",2200,"CE")
and sharing the same name :
like output
(10,"Anitha",1000,"AE")
(70,"Anitha",2300,"BE")
(80,"Anitha",2400,"ME")
(20,"Chaitanya",2000,"SE");
(30,"Chaitanya",3000,"SE");
(40,"Deepthi",2100,"AE");
(50,"Deepthi",2200,"CE");
(60,"Deepthi",2300,"BE");
using collections....
Will some one be able to help me?
Thanks in Advance.
Nithya
If you're using java 8 please skip to the end!
I'd probably create a map to do this, but it seems like you're new to Java so I'll describe the more basic way.
You should first create a list(arraylist) like so:
// create an arraylist (list based on an array)
List<Emp> emps = new ArrayList<Emp>();
Then you can add objects to the list:
emps.add(new Emp(10,"Anitha",1000,"AE"));
emps.add(new Emp(20,"Chaitanya",2000,"SE"));
.
.
Now you can start filtering!
So, suppose you have a getName() method in class Emp, you can write a function such as this:
// this function takes a list and a name, and filters the list by the name
public static List<Emp> filterEmpsByName(List<Emp> emps, String name){
// a place to store the result
List<Emp> result = new ArrayList<Emp>();
// iterate over the list we got
for (Emp emp: emps){
// save only the elements we want
if (emp.getName().equals(name)){
result.add(emp);
}
}
return result;
}
Now, filtering would be a simple matter of calling that function:
// print to standard output the result of our function on the "main" list `emp` with name "Anitha"
for (Emp emp : filterEmpsByName(emps, "Anitha")){
System.out.println(emp.toString()); // make sure toString() is overridden in Emp class
}
Now for the second part which is a bit more tricky:
// this function takes a list and a name, and filters the list by the name
public static List<Emp> getDistinctlyNamedEmps(List<Emp> emps, String name) {
// this time we use a map which is A LOT faster for this kind of operation
Map<String, Emp> result = new HashMap<String, Emp>();
// iterate over the list we got
for (Emp emp : emps) {
// save only the elements we want
if (result.get(emp.getName()) == null ) {
result.put(emp.getName(), emp);
}
}
// convert map to list - not mandatory if you can use the map as is...
return new ArrayList<Emp>(result.values());
}
Notice that you can also write a comparator that compares objects using the name/any other property, but that's beyond the scope of this comment :-)
Putting the whole thing together:
Main class:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
// create an [arraylist][4] (list based on an array)
List<Emp> emps = new ArrayList<Emp>();
emps.add(new Emp(10, "Anitha", 1000, "AE"));
emps.add(new Emp(20, "Chaitanya", 2000, "SE"));
// print to standard output the result of our function on the "main"
// list `emp` with name "Anitha"
System.out.println("filterEmpsByName(emps, \"Anitha\") output:");
for (Emp emp : filterEmpsByName(emps, "Anitha")) {
System.out.println(emp.toString()); // make sure toString() is
// overridden in Emp class
}
// print to standard output the result of our second function on the "main"
// list `emp`
System.out.println("getDistinctlyNamedEmps(emps) output:");
for (Emp emp : getDistinctlyNamedEmps(emps)) {
System.out.println(emp.toString()); // make sure toString() is
// overridden in Emp class
}
}
// this function takes a list and a name, and filters the list by the name
public static List<Emp> filterEmpsByName(List<Emp> emps, String name) {
// a place to store the result
List<Emp> result = new ArrayList<Emp>();
// iterate over the list we got
for (Emp emp : emps) {
// save only the elements we want
if (emp.getName().equals(name)) {
result.add(emp);
}
}
return result;
}
// this function takes a list and a name, and filters the list by the name
public static List<Emp> getDistinctlyNamedEmps(List<Emp> emps) {
// this time we use a map which is A LOT faster for this kind of
// operation
Map<String, Emp> result = new HashMap<String, Emp>();
// iterate over the list we got
for (Emp emp : emps) {
// save only the elements we want
if (result.get(emp.getName()) == null) {
result.put(emp.getName(), emp);
}
}
// convert map to list - not necessary
return new ArrayList<Emp>(result.values());
}
}
And partial Emp class:
public class Emp {
private String name;
public Emp(int stubi, String name, int j, String stubs) {
this.name = name;
}
public String getName() {
return this.name;
}
public String toString() {
return "[" + this.name + "]";
}
}
Java 8:
Java 8 has lambda expressions (anonymous functions), which are neat tools used in many other languages for filtering as well as other operations.
You can read more about using them here.
As far as I can see, the answers until now assumed that the task was to search for a particular name, or to find elements with unique names - and I think this was not what was asked for.
In order to filter a list in the way that is described in the original question, one could create a map from the "key" (that is, the 'name' in this case) to a list of elements that share this key. Using this map, one can easily find
One element for each key that occurs only once in all elements
The list of all elements that have a key that occurs at least twice in all elements
These tasks are rather similar, and further generalizations may be possible, but here is one way of how this may be implemented:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class CriterionFilter
{
public static void main(String[] args)
{
List<Emp> list = new ArrayList<Emp>();
list.add(new Emp(10,"Anitha",1000,"AE"));
list.add(new Emp(20,"Chaitanya",2000,"SE"));
list.add(new Emp(30,"Chaitanya",3000,"SE"));
list.add(new Emp(40,"Deepthi",2100,"AE"));
list.add(new Emp(50,"Deepthi",2200,"CE"));
list.add(new Emp(60,"Deepthi",2300,"BE"));
list.add(new Emp(70,"Anitha",2300,"BE"));
list.add(new Emp(80,"Anitha",2400,"ME"));
list.add(new Emp(90,"Sita",2200,"CE"));
list.add(new Emp(100,"Hari",2200,"CE"));
list.add(new Emp(110,"Krishna",2200,"CE"));
Function<Emp, String> keyFunction = new Function<Emp, String>()
{
#Override
public String apply(Emp s)
{
return s.getName();
}
};
List<Emp> fiteredOnUnique = filterOnUnique(list, keyFunction);
System.out.println("Filtered on unique:");
print(fiteredOnUnique);
List<Emp> filteredOnSame = filterOnSame(list, keyFunction);
System.out.println("Filtered on same:");
print(filteredOnSame);
}
private static void print(Iterable<?> elements)
{
for (Object element : elements)
{
System.out.println(element);
}
}
/**
* Create a map that maps the keys that are provided for the given
* elements to the list of elements that have this key
*
* #param elements The input elements
* #param keyFunction The key function
* #return The map
*/
private static <T, K> Map<K, List<T>> map(
Iterable<? extends T> elements, Function<? super T, K> keyFunction)
{
Map<K, List<T>> map = new HashMap<K, List<T>>();
for (T t : elements)
{
K key = keyFunction.apply(t);
List<T> list = map.get(key);
if (list == null)
{
list = new ArrayList<T>();
map.put(key, list);
}
list.add(t);
}
return map;
}
/**
* Uses the given key function to compute the keys associated with the
* given elements, and returns a list containing the element of
* the given sequence that have unique keys
*
* #param elements The input elements
* #param keyFunction The key function
* #return The filtered list
*/
private static <T, K> List<T> filterOnUnique(
Iterable<? extends T> elements, Function<? super T, K> keyFunction)
{
List<T> result = new ArrayList<T>();
Map<K, List<T>> map = map(elements, keyFunction);
for (Entry<K, List<T>> entry : map.entrySet())
{
List<T> list = entry.getValue();
if (list.size() == 1)
{
result.add(list.get(0));
}
}
return result;
}
/**
* Uses the given key function to compute the keys associated with the
* given elements, and returns a list containing all elements of
* the given sequence that have a key that occurs multiple times.
*
* #param elements The input elements
* #param keyFunction The key function
* #return The filtered list
*/
private static <T, K> List<T> filterOnSame(
Iterable<? extends T> elements, Function<? super T, K> keyFunction)
{
List<T> result = new ArrayList<T>();
Map<K, List<T>> map = map(elements, keyFunction);
for (Entry<K, List<T>> entry : map.entrySet())
{
List<T> list = entry.getValue();
if (list.size() > 1)
{
result.addAll(list);
}
}
return result;
}
/**
* Interface for a generic function
*/
static interface Function<S, T>
{
T apply(S s);
}
}
class Emp
{
private int i;
private String name;
private int j;
private String whatever;
public Emp(int i, String name, int j, String whatever)
{
this.i = i;
this.name = name;
this.j = j;
this.whatever = whatever;
}
#Override
public String toString()
{
return "Emp [i=" + i + ", name=" + name + ", j=" + j + ", whatever=" + whatever + "]";
}
String getName()
{
return name;
}
}
(EDIT: Adjusted the first case, see comment in original question)
Thats not a collections, while it is a 'list' in the traditional sense, it is not a java.util.ArrayList.
To do this, you might try:
ArrayList<Emp> employeeList = new ArrayList<Emp>();
employeeList.add(new Emp(10,"Anitha",1000,"AE"));
// repeat