0

I have an ArrayList of HashMaps 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()
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
Michael Mo
  • 23
  • 7
  • `Stream#max` takes a `Comparator` object. You could pass it something like `Comparator.comparingInt(Map::length)`. If you're new to programming (and not just Java), I'd suggest trying to use a `for` loop rather than mucking with streams right now. – Rogue Nov 03 '22 at 13:48
  • You can achieve everything that stream does with for loop with better performance btw so start with that. Also it's easier to learn starting from for loop then going to streams – Morph21 Nov 03 '22 at 13:50

2 Answers2

2

Use the Power of Objects

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.

Find a single Max element

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

Find a group of elements

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"?

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
1

In one shot, you can use Collectors.groupingBy like so:

List<HashMap<String, Object>> response = resultslist.stream()
        .collect(Collectors.groupingBy(HashMap::size))
        .entrySet().stream()
        .max(Map.Entry.comparingByKey())
        .map(Map.Entry::getValue)
        .orElseGet(Collections::emptyList);

The idea is, to group by size (This step returns a Map of Integer and List of Map), then get the max of this map.

Youcef LAIDANI
  • 55,661
  • 15
  • 90
  • 140