2

I'm currently struggling with the following excercise:

Given a Stream<String> collect (using Collector) a Collection of all Strings with maximum lenght.

Here is what I tried:

private static class MaxStringLenghtCollector 
                    implements Collector<String, List<String>, List<String>> {

    @Override
    public Supplier<List<String>> supplier() {
        return LinkedList::new;
    }
    @Override
    public BiConsumer<List<String>, String> accumulator() {
        return (lst, str) -> {
            if(lst.isEmpty() || lst.get(0).length() == str.length())
                lst.add(str);
            else if(lst.get(0).length() < str.length()){
                lst.clear();
                lst.add(str);
            }                   
        };
    }

    @Override
    public BinaryOperator<List<String>> combiner() {
        return (lst1, lst2) -> {
            lst1.addAll(lst2);
            return lst1;
        };
    }

    @Override
    public Function<List<String>, List<String>> finisher() {
        return Function.identity();
    }

    @Override
    public Set<java.util.stream.Collector.Characteristics> characteristics() {
        return EnumSet.of(Characteristics.IDENTITY_FINISH);
    }
}

So I wrote my custom collector which does the job but... it really does look ugly. Maybe there's some standard way to do that. For instance, I'd try the grouping collector:

public static Collection<String> allLongest(Stream<String> str){
    Map<Integer, List<String>> groups = str.collect(Collectors.groupingBy(String::length));
    return groups.get(groups.keySet()
                    .stream()
                    .mapToInt(x -> x.intValue())
                    .max()
                    .getAsInt());
}

But this is ugly as well as inefficient. First, we build a Map, then travesrse it to build a Set and then traverse it get the max-List.

Binary Nerd
  • 13,872
  • 4
  • 42
  • 44
stella
  • 2,546
  • 2
  • 18
  • 33
  • 3
    I believe this answers your question http://stackoverflow.com/questions/29334404/how-to-force-max-to-return-all-maximum-values-in-a-java-stream You can use the answers there with a comparator comparing the length of the Strings. But yes, you need to have a custom collector if you want to do it one-pass. – Tunaki May 24 '16 at 15:07

1 Answers1

4

I would do it like this:

List<String> values = Arrays.asList("abc", "ab", "bc", "bcd", "a");
// I group by length and put it into a TreeMap then get the max value
values.stream().collect(groupingBy(String::length, TreeMap::new, toList()))
    .lastEntry()
    .getValue()
    .forEach(System.out::println);

Output:

abc
bcd
Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122