passing in two lambda functions - java

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());

Related

Unable to get my head around Predicate isEqual method

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

How to return specific attributes from a stream method

I made a method in my item class which filters through my Array list items which has less than 2 quantities.
The method is returning all the attributes of that item when I want only its name and price.
I'm stuck on what needs to be change in my method
public static List<item> findquantity(List<item> items) {
return items.stream()
.filter(item -> item.getquantity()< 2)
.collect(Collectors.toList());
}
I am calling the method in my main as
System.out.println(item.findquantity(itemDatabase));
If you change the return type of findQuantity to Map<String, Double> instead of List<Item>, you can use Collectors.toMap() to return only name and price :
public static Map<String, Double> findQuantity(List<Item> items) {
return items.stream().filter(item -> item.getQuantity() < 2)
.collect(Collectors.toMap(Item::getName, Item::getPrice));
}
A better way is to create a custom object that holds the values as referred in the comments by #Thomas. Should be something like this:
items.stream().filter(item -> item.getQuantity() < 2).map(i -> new CustomItem(i.getName(), i.getPrice()))
.collect(Collectors.toList());
You can try this:
First you have to make a class that contain two fields, price and name, example:
Class Price{
String name;
BigDecimal price;// I don't Know is your quantity is int, Double or Float
//Any other property that you need
//Constructor
//Getter & Setter
}
And Modifique your Mehtod:
public static List<Price> findquantity(List<item> items) {
List<Price> lstPrice = new ArrayList<>();
items.stream().filter(it -> it.getquantity()< 2).map((vals) -> {
Price item = new Price();
item.setName(vals.getName());
item.setPrice(vals.getquantity());
//set any other property that you need
return item;
}).forEachOrdered((item) -> {
lstPrice.add(item);
});
return lstPrice;
}
That's all, I hope that help you

Java sort array of objects by integer field and if those are identical then sort by another integer value

I have a java problem in relation too sorting an arrayList of objects.
I have already figured out to sort an arrayList of object by specific fields, which can be seen in the following code
public void printPrioritized() {
System.out.println("Prioritized todo:");
System.out.println("-----------------");
List<Task> sortedList = new ArrayList<Task>(taskList);
Collections.sort(sortedList, new Comparator<Task>() {
public int compare(Task o1, Task o2) {
return Integer.valueOf(o1.getPriority()).compareTo(o2.getPriority());
}
});
sortedList.forEach((e) -> {
System.out.println(e);
});
My problem is that if to object fields are the same then i am supposed to sort by another value. This means that i have to sort by an value of 1 to 4 (getPriority() method), but if two objects for instance both are 2 then i have to sort by another value which for instance could be time. Hope someone can help.
Assuming your Task class looks something like:
class Task {
int priority;
int anotherValue;
// getters, setters ...
}
you can create custom compartors and chain them while sorting, example:
List<Task> myList = new ArrayList<>();
Comparator<Task> byPriority = (t1,t2) -> Integer.compare(t1.getPriority(), t2.getPriority());
Comparator<Task> byAnotherValue = (t1,t2) -> Integer.compare(t1.getAnotherValue(), t2.getAnotherValue());
myList.sort(byPriority.thenComparing(byAnotherValue));
OR
you can combine those sortings ->
List<Task> myList = new ArrayList<>();
Comparator<Task> sortedComparator = (t1,t2) -> {
if (t1.getPriority() != t2.getPriority()) {
return Integer.compare(t1.getPriority(), t2.getPriority());
}
else if (t1.getAnotherValue() != t2.getAnotherValue()) {
return Integer.compare(t1.getAnotherValue(), t2.getAnotherValue());
}
};
myList.sort(sortedComparator);
Try to customize the compare method.
e.g.
if(o1.getPriority() != o2.getPriority())
return Integer.valueOf(o1.getPriority()).compareTo(o2.getPriority());
if(o1.getTime() != o2.getTime())
return Integer.valueOf(o1.getTime()).compareTo(o2.getTime());
return 0; //they are equal with all fields

Rewrite existing code with Java 8 Lambda

I have one object, two conditions and need to convert it into a list.
SelectItem has getLabel and getValue
Casting problem
Expect Result -> List of Employees
Object refer to class (Example: Employees.class)
The code looks like:
public static final List<Employees> onFilterObjectFromSelectItems(final String query,final List<SelectItem> selectItemList) {
final List <Employees>result = new ArrayList<>();
for (SelectItem sl : selectItemList) {
Employees x = (Employees) sl.getValue();
if (x.getCode.contains(query) || x.getName.contains(query)) {
result.add(x);
}
}
return result;
}
Try following code.
List<Employees> EmployeeList = selectItemList.stream()
.filter(x-> x.getCode().contains(query) || x.getName().contains(query))
.map(e->(Employees)e.getValue())
.collect(Collectors.toList());

In java, is there a way to search for a specific field of a class stored in a linked list?

I wrote a class that is to be stored in a linkedlist, with 3 fields in the class. One of these fields is a String, which I would like to search for in the linked list.
Example
LinkedList
Obj1
String name = "first";
int age = 2;
int size = 4;
Obj2
String name = "second";
int age = 3;
int size = 6;
Obj3
String name = "third";
int age = 5;
int size = 8;
If this is the linkedlist storing these three objects with the given fields, is there a way to search the linked list for the object with the name "second"?
You can search for an item in the list by iteration
// Iterate over each object within the list
for(YourClass obj : yourLinkedList) {
// Check if the object's name matches the criteria, in this case, the name
// of the object has to match "second"
if (obj.name.equals("second")) {
// If we are within this block, it means that we found the object that has
// its name set as "second".
return obj;
}
}
You could also make a method to make things more elegant
public YourClass findByName(String name) {
for(YourClass obj : yourLinkedList) {
if (obj.name.equals(name)) {
return obj;
}
}
return null;
}
And use it the following way
YourClass object = findByName("second");
The easiest way to do this would be to of course, iterate through each element in the collection, checking if it matched your filter condition, and selecting the matches found. However this gets tedious the more times you need to do it, and the more complex your filter condition is. I would recommend utilizing pre-existing libraries to get the task done efficiently. Here is an example using Google-Collections:
final List<SomeObj> listObjs = Arrays.asList(
new SomeObj("first", 2, 4), new SomeObj("second", 3, 6),
new SomeObj("third", 5, 8));
final Iterable<SomeObj> filtered = Iterables.filter(listObjs,
new Predicate<SomeObj>() {
#Override
public boolean apply(final SomeObj obj) {
return "second".equals(obj.getName());
}
});
for (final SomeObj obj : filtered) {
System.out.println(obj);
}
The code shown would select all objects in the list with a name property of "second". Obviously, the predicate doesn't have to be an anonymous inner class - if you needed to reuse it you would just break it out to a standalone class.
Here's another way to implement a Comparator (just in case it helps).
I find it's easier to understand if you implement the Comparator explicitly:
class PersonAgeComparator implements Comparator<Person> {
#Override
public int compare(Person p1, Person person2) {
return p1.getAge().compareTo(p2.getAge());
}
}
You might use the above like this:
Comparator ageComparator = new PersonAgeComparator();
List<Person> personList = // populate list somehow
Person fourYearOld = new Person();
fourYearOld.setAge(4);
for (Person p : personList) {
if (ageComparator.compare(fourYearOld, p) == 0) {
System.out.println(p.getName() + " is 4 years old");
}
}
This doesn't make much sense for this simple example.
It would be ideal if you had several complicated ways to compare people (by height, by adjusted income, by how many states they've lived in, etc...).
Take a look at the java.util.Comprator interface. You can write a method that iterates over a List and uses a comparator to find the one you are after.
Something like (not compiled):
for(final T value : list)
{
if(comparator.compare(value, desired) == 0)
{
// match
}
}
In your comparator you have it perform whatever comparison you want.
Here is a working example:
public class JavaApplication4
{
public static void main(String[] args)
{
final List<Data> list;
final List<Data> a;
final List<Data> b;
list = new ArrayList<Data>();
list.add(new Data("Foo", 1));
list.add(new Data("Bar", 10));
list.add(new Data("Car", 10));
a = find(list,
new Data("Bar", 0),
new Comparator<Data>()
{
#Override
public int compare(final Data o1,
final Data o2)
{
return (o1.name.compareTo(o2.name));
}
});
b = find(list,
new Data(null, 10),
new Comparator<Data>()
{
#Override
public int compare(final Data o1,
final Data o2)
{
return (o1.count - o2.count);
}
});
System.out.println(a.size());
System.out.println(b.size());
}
private static List<Data> find(final List<Data> list,
final Data desired,
final Comparator<Data> comprator)
{
final List<Data> results;
results = new ArrayList(list.size());
for(final Data data : list)
{
if(comprator.compare(desired, data) == 0)
{
results.add(data);
}
}
return (results);
}
private static class Data
{
private final String name;
private final int count;
Data(final String nm,
final int c)
{
name = nm;
count = c;
}
}
}
And here is a generic version of the find method. Using this method you would never have to write the find method again, using a method that embeds the logic for matching in the iteration code means that you would have to re-write the iteration logic for each new set of matching logic.
private static <T> List<T> find(final List<T> list,
final T desired,
final Comparator<T> comprator)
{
final List<T> results;
results = new ArrayList(list.size());
for(final T value : list)
{
if(comprator.compare(desired, value) == 0)
{
results.add(value);
}
}
return (results);
}
You can go through it and get it done or there's another way.
You need to override the equals method in your class (and the hashcode method as well).
After you override the equals to your desire, in this case to compare the names, create a new object with the same name and call the remove(Object o) method of the LinkedList and get the object.
You should note that with this approach you objects equality will be defined by name and that the entry will be removed from the LinkedList

Categories

Resources