I have two lists,
Map<String, String> map1 = new HashMap<>();
map1.put("age", "30");
map1.put("name", "john");
Map<String, String> map2 = new HashMap<>();
map1.put("age", "31");
map1.put("name", "marry");
List<Map<String, String>> list1 = new ArrayList<>();
list1.add(map1);
list1.add(map2);
Map<String, String> map3 = new HashMap<>();
map1.put("age", "40");
map1.put("name", "mike");
Map<String, String> map4 = new HashMap<>();
map1.put("age", "41");
map1.put("name", "terry");
List<Map<String, String>> list2 = new ArrayList<>();
list1.add(map3);
list1.add(map4);
I want to iterate through the list1 and find if age is found. If age is found, it should return the name.
If age is not found in the list1, it should iterate through the list2, and if found return the name.
list1.stream().filter(a -> a.get("age").equals("40")).findAny().ifPresentOrElse()
What is the best to filter using a java stream?
CodePudding user response:
Use the Power of Objects
The usage of maps in your code is an example of abuse of collections. Storing the data in such a way is inconvenient and error-prone.
Instead of trying to substitute a domain object with a Map
, you need to define a class
(or a record
).
It'll give you many advantages that you're depriving yourself by using maps:
- Ability to use the proper type for every property instead of keeping everything as strings;
- No need to perform parsing;
- Self-explanatory method names instead of hard-coded string keys, and your code will not fail because you've misspelled a key;
- The code is easier to read and maintain.
For the sake of simplicity and conciseness, I'll go with a Java 16 record:
public record Person(String name, int age) {}
And now we have two lists of Person
instead of two lists of maps.
To implement the logic when we're starting with examining the first list and only if a result was not found we proceed by iterating through the second list, we can make use of the Java 9 method or()
defined by Optional
. While invoked on the optional object containing a value, or()
returns the same optional, otherwise it'll return an optional produced by a supplier provided as an argument.
For convenience, we can define a method that takes a List<Person>
and the target age
and returns an optional result.
public static Optional<Person> getPersonByAge(List<Person> people, int age) {
return people.stream().filter(pers -> pers.age() == age).findFirst();
}
We can make this method reusable by making it generic. So it would expect a List<T>
and a Predicate<T>
as its arguments.
public static <T> Optional<T> getPersonByAge(List<T> people,
Predicate<T> predicate) {
return people.stream().filter(predicate).findFirst();
}
And that how we can apply it:
List<Person> list1 = List.of(new Person("john", 30), new Person("marry", 31));
List<Person> list2 = List.of(new Person("mike", 40), new Person("terry", 41));
Predicate<Person> age40 = pers -> pers.age() == 40;
String name = getPersonByAge(list1, age40)
.or(() -> getPersonByAge(list2, age40))
.orElseThrow()
.name();
System.out.println(name);
Output:
mike
CodePudding user response:
If you want to use the map so you can:
Optional<String> nameByAge = getNameByAge(list1, "40").or(()->getNameByAge(list2, "40"));
public static Optional<String> getNameByAge(List<Map<String, String>> people, String age) {
return people.stream().filter(person -> person.get("age").equals(age)).findAny().map(person-> person.get("name"));
}