minizibi
minizibi

Reputation: 671

Show all the longest words from finite Stream

I have to find all longest words from given file using Streams API. I did it in few steps but looking for some "one liner", actually I processing whole file two times, first to find max length of word and second for comparing all to max length, assuming its not the best for performance ; P Could someone help me? Just look at code:

public class Test {
    public static void main(String[] args) throws IOException {
        List<String> words = Files.readAllLines(Paths.get("alice.txt"));
        OptionalInt longestWordLength = words.stream().mapToInt(String::length).max();
        Map<Integer, List<String>> groupedByLength = words.stream().collect(Collectors.groupingBy(String::length));
        List<String> result = groupedByLength.get(longestWordLength.getAsInt());
    }
}

I wish to make it straight:

List<String> words = Files.readAllLines(Paths.get("alice.txt"));
List<String> result = // code

File contains just one word per line, anyway it's not important - question is about the right stream code.

Upvotes: 1

Views: 2808

Answers (4)

Saxena Himanshu
Saxena Himanshu

Reputation: 1

public static void main(String[] args) {
    // TODO Auto-generated method stub

    String s = "hello himanshu i am in Pune";
    
String longest = Arrays.stream(s.split(" ")).max(Comparator.comparingInt(String::length)).orElse(null).toString();

System.out.println(longest+" and the length- "+longest.length());

Upvotes: 0

Mureinik
Mureinik

Reputation: 311393

Instead of just keeping the largest length, you could collect the words into a map from their length to the words, and then just take the longest one:

List<String> longestWords =
    Files.lines(Paths.get("alice.txt"))
         .collect(Collectors.groupingBy(String::length))
         .entrySet()
         .stream()
         .sorted(Map.Entry.<Integer, List<String>> comparingByKey().reversed())
         .map(Map.Entry::getValue)
         .findFirst()
         .orElse(null);

EDIT:
As Malte Hartwig noted, using max on the streamed map is much more elegant (and probably faster):

List<String> longestWords =
    Files.lines(Paths.get("alice.txt"))
         .collect(Collectors.groupingBy(String::length))
         .entrySet()
         .stream()
         .max(Map.Entry.comparingByKey())
         .map(Map.Entry::getValue)
         .orElse(null);

EDIT2:
There's a built-in inefficiency in both the above solutions, as they both build a map a essentially store the lengths for all the strings in the file instead of just the longest ones. If performance is more important than elegance in your usecase, you could write your own Collector to just preserve the longest strings in list:

private static int stringInListLength(List<String> list) {
    return list.stream().map(String::length).findFirst().orElse(0);
}

List<String> longestWords =
    Files.lines(Paths.get("alice.txt"))
         .collect(Collector.of(
             LinkedList::new,
             (List<String> list, String string) -> {
                 int stringLen = string.length();
                 int listStringLen = stringInListLength(list);
                 if (stringLen > listStringLen) {
                     list.clear();
                 }
                 if (stringLen >= listStringLen) {
                     list.add(string);
                 }
             },
             (list1, list2) -> {
                 int list1StringLen = stringInListLength(list1);
                 int list2StringLen = stringInListLength(list2);
                 if (list1StringLen > list2StringLen) {
                     return list1;
                 }
                 if (list2StringLen > list1StringLen) {
                     return list2;
                 }
                 list1.addAll(list2);
                 return list1;
             }
         ));

Upvotes: 7

Alberto S.
Alberto S.

Reputation: 7649

reduce will help you:

    Optional<String> longest = words.stream()
            .reduce((s1, s2) -> {
                if (s1.length() > s2.length())
                    return s1;
                else
                    return s2;
            });

In case the Stream is empty it will return an Optional.empty

In case you want the list of all the words that have the maximum length this piece will help you:

    Optional<List<String>> longest = words.stream()
            .collect(Collectors.groupingBy(
                    String::length,
                    Collectors.toList()
            ))
            .entrySet()
            .stream()
            .reduce(
                    (entry1, entry2) -> {
                        if (entry1.getKey() > entry2.getKey())
                            return entry1;
                        else
                            return entry2;
                    }
            )
            .map(Map.Entry::getValue);

Upvotes: 3

Linusk
Linusk

Reputation: 71

iterate over the map keys to find the longest wordlength

Upvotes: 0

Related Questions