2

I've been stuck with one lambda expression and the Comparator class using Comparator.comparing(...).thenComparing(...) methods to sum up two way of sorting a Stream.

Both of my methods are working, but when I put them together nothing is working at all.

Here is the link if you want to try and validate the exercise:

http://codecheck.it/codecheck/files?repo=heigvdcs1&problem=poo3e

And here is what you have to do:

For each word in a stream, determine the “vowelness”, i.e. the number of vowels - the number of consonants. Produce the n words with the highest vowelness paired with the vowelness value. Sort first by vowelness, then by the string. Complete this program. This time, you have a hidden static method long Words.vowels(String w) at your disposal that yields the number of vowels in w, including duplicates.

For now i have managed to do this:

import java.util.*;
import java.util.stream.*;

public class Streams
{
   List<Pair<String, Long>> wordsWithManyVowels(Stream<String> words, int n)
   {
      return words
         .map( w -> Pair.of( w , ( Words.vowels(w) - ( w.length() - Words.vowels(w)))))
         .sorted(Comparator.comparingLong(f1 -> -f1.second())
         //This part is working without the first comparing
         //.thenComparing(f2 -> f2.first().length()))
         .limit(n)
         .collect(Collectors.toList());
   }
}

The Pair class:

import java.util.Objects;

public class Pair<T, S> 
{
   private T first;
   private S second;

   public Pair(T firstElement, S secondElement)
   {
      first = firstElement;
      second = secondElement;
   }

   /*
      Use Pair.of(x, y) instead of new Pair<...,...>(x, y)
      so you get the type inferred
   */

   public static <T, S> Pair<T, S> of(T firstElement, S secondElement)
   {
      return new Pair<T, S>(firstElement, secondElement);
   }

   public T first() { return first; }
   public S second() { return second; }

   public String toString() { return "(" + first + "," + second + ")"; }

   public boolean equals(Object otherObject)
   {
      if (this == otherObject) 
         return true;
      if (otherObject == null || !(otherObject instanceof Pair)) 
         return false;
      @SuppressWarnings("unchecked") Pair<T, S> other = (Pair<T, S>) otherObject;
      return Objects.equals(first, other.first) &&
         Objects.equals(second, other.second);
   }
}
4
  • What does "nothing is working at all" mean? What happens? Exceptions, compile errors, wrong sort order? Commented Jun 14, 2017 at 14:28
  • Your f2 -> f2.first().length()) function is an int extractor, thus shouldn't the method be : thenComparingInt ? Commented Jun 14, 2017 at 14:42
  • @MalteHartwig It means that method are not working togather. Although the are working separately Commented Jun 14, 2017 at 15:41
  • 1
    Just a side note: don’t use negation to reverse the order. In case of Long.MIN_VALUE, it doesn’t work due to overflow, the same applies to the minimum values of the other integer types. It’s tempting to say that this will never happen for a particular property, but it will cost you a lot of time once this assumption doesn’t hold anymore and you don’t remember that you made this assumption somewhere in your code. You may just use Comparator.comparingLong(Pair<String, Long>::second) .reversed() .thenComparingInt(p -> p.first().length()) Commented Jun 14, 2017 at 16:26

1 Answer 1

3

Solution 1

Extracting comparing methods as static works like this

public class Streams {
    List<Pair<String, Long>> wordsWithManyVowels(Stream<String> words, int n) {
        return words
            .map(w -> Pair.of(w, (Words.vowels(w) - (w.length() - Words.vowels(w)))))
            .sorted(Comparator.comparingLong(Streams::vowelness).thenComparingInt(Streams::length))
            .limit(n)
            .collect(Collectors.toList());
    }

    static int length(Pair<String, Long> p) {
        return p.first().length();
    }

    static long vowelness(Pair<String, Long> p) {
        return -p.second();
    }
}

Solution 2

Use this Comparator implementation without static methods

Comparator
    .comparingLong((Pair<String, Long> p) -> -p.second())
    .thenComparingInt((Pair<String, Long> p) -> p.first().length())

Note: see how thenComparingInt is used at the end of the composite comparator in the both solutions.

Solution 3

The problem of the source code is static Comparator methods need info about processing elements' type in a chain. By default Object type is used. So, it's possible to specify it this way (simpler than in Solution 2):

Comparator.<Pair<String, Long>>comparingLong(p -> -p.second()).thenComparing(p -> p.first().length())
Sign up to request clarification or add additional context in comments.

5 Comments

Andriy, could you explain what exactly the error was in Thomas' code?
@MalteHartwig pls, see Solution 3: here is compilation error as compiler can't understand needed Pair type in chained static methods.
@AndriyKryvtsun Thank you for your help, I'm understanding better now. But you solutions are working find although they are sorting string by length and i need them to be alphabeticaly sorted. That was, why i wasn't using a thenCampingInt(). But thank you i will find how to do it from that point
@ThomasL if you need to compare strings in alphabetical order just use this clause .thenComparing(Pair::first). So the whole comparator can looks like this Comparator.<Pair<String, Long>>comparingLong(Pair::second).reversed().thenComparing(Pair::first)
@AndriyKryvtsun Thanks i do it like this: Comparator.comparingLong((Pair<String, Long> p) -> -p.second()) .thenComparing((Pair<String, Long> p) -> p.first())

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.