I have an ArrayList
of HashMap
s and each HashMap
looks like:
{"Start":"A", "End":"B","Length":5}
I want to find the one that has the longest length, or maybe more than one, they all have the same length equal to the max length.
Trying to use stream, how should I do it?
ArrayList<HashMap<String, Object>> resultslist = new ArrayList<HashMap<String, Object>>();
ArrayList<HashMap<String, Object>> finalresult = resultslist.stream().max()
hashmap looks like
{"Start":"A", "End":"B","Length":5}
You are misusing Maps, it definitely should be a custom object, with attributes having proper types instead of storing them as java.lang.Object
in the map.
For example, that how it might look like, if we implemented as a Java 16 record:
public record Foo(String start, String end,int length) {}
Now things would be simple, instead of a nested collection you would have a list of Foo
.
To find a Foo
with maximum length
you can use either Stream.max()
or Collections.max()
, both expect an instance of Comparator
as an argument.
List<Foo> foos = // intializing the list
// with Stream API
Foo max = foos.stream()
.max(Comparator.comparingInt(Foo::length)) // produces Optional<Foo>
.orElseThrow();
// using Collections.max()
Foo max = Collections.max(foos, Comparator.comparingInt(Foo::length));
If you want to obtain a Collection of objects having the largest value of length
, then it would require a bit more effort.
For that can group the data by length
into an intermediate Map by using Collector groupingBy()
, then create a stream over map entries and pick the entry with the highest key using Stream.max()
(like it has been done above):
List<Foo> foos = // intializing the list
List<Foo> max = foos.stream()
.collect(Collectors.groupingBy(Foo::length)) // Map<Integer, List<Foo>>
.entrySet().stream() // Stream<Map.Entry<Integer, List<Foo>>>
.max(Map.Entry.comparingByKey()) // Optional<Map.Entry<Integer, Foo>>
.map(Map.Entry::getValue) // Optional<List<Foo>>
.orElse(Collections.emptyList());
Alternatively, it can be done without creating an intermediate Map and the second stream over its entries.
To achieve that we can use the three-args version of Stream.collect()
and accumulate stream elements into a list that would be returned as the result of the stream execution:
List<Foo> foos = // intializing the list
List<Foo> max = foos.stream()
.collect(
ArrayList::new,
(List<Foo> l, Foo f) -> {
if (!l.isEmpty() && l.get(0).length() < f.length()) l.clear();
if (l.isEmpty() || l.get(0).length() == f.length()) l.add(f);
},
(l, r) -> {
if (l.get(0).length() < r.get(0).length()) l.clear();
if (l.isEmpty() || l.get(0).length() == r.get(0).length()) l.addAll(r);
}
);
Sidenote: you might also want to learn What does it mean to "program to an interface"?