How i can create String comparator through lambda? - java

I am trying to sort list of string through my own comparator. I saw the below template for list of developers.
//lambda
listDevs.sort((Developer o1, Developer o2)->o1.getAge()-o2.getAge());
My code: (this is giving me compile error)
List<String> s = new ArrayList<>();
Collections.sort(s, (String a, String b)-> {
return a.length() > b.length()
});
This code is not compiling. Can someone help me what is wrong with what I am doing?

Arrays.sort is used for sorting Arrays not for Collection objects, use Collections.sort to sort ArrayList
Collections.sort(s,(a,b)->a.length()-b.length());
And corresponding lambda expression is wrong (String a,String b)-> a.length() > b.length() which returns Boolean value. Where Comparator.compare should return int value. And also the type of the parameters can be explicitly declared or it can be inferred from the context.
int compare(T o1, T o2)
You can also use List.sort
s.sort((a,b)->a.length()-b.length());

There are 2 unrelated problems with your code:
[1] There are two ways to write lambdas.
You can either write a single expression serving as the return value:
s.sort((a, b) -> a.length() - b.length());
Or you can involve a code block; but if you do that, return (and semicolons) have to show up:
s.sort((a, b) -> {
return a.length() - b.length();
});
Generally if you can make it work with a single expression, just do that; it's shorter. In your example you're using braces (so, the second form), but you don't have the return keyword nor the required semicolon.
Also, the types of the lambda arguments are optional. Generally if from context it is clear, just omit these: (a, b) -> a.length() - b.length()
[2] a comparator should return 0 if the arguments are equal, a negative number (any negative number, it doesn't matter which one) if a is 'smaller' than b, and a positive number if a is 'larger' than b. You are returning a boolean (you are using a greater than sign; the result of that expression is 'true' or 'false'; you need a number instead.
The solution seems trivial here: use minus instead.
[3] a bonus tip: There are utility methods for comparing based on field: Comparator.comparingInt(String::length) will do what you want in a way that is easier to read and less error prone. Also, instead of Collections.sort(list, comparator) you can just write list.sort(comparator); easier on the eyes, shorter, and more idiomatic.
Let's put it all together:
List<String> s = new ArrayList<>();
s.sort(Comparator.comparingInt(String::length));

Related

How does this #Override for Arrays.sort work in Java?

Arrays.sort(people, (n1, n2) -> (n2[0] == n1[0])? n1[1] - n2[1] : n2[0] - n1[0]);
or
Arrays.sort(people,new Comparator<int[]>(){
#Override
public int compare(int[] n1, int[] n2){
return (n2[0] == n1[0])? n1[1] - n2[1]: n2[0] - n1[0];
}
});
Both these perform the same operation.
Input: [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
Output: [[7,0],[7,1],[6,1],[5,0],[5,2],[4,4]]
I know the code is sorting it in groups but I don't understand how. I was similarly confused about PriorityQueue in Java:
PriorityQueue<Integer> pq = new PriorityQueue<>((a, b)-> b - a);
This one sorts in decreasing order.
Can someone explain it? Where can I study or read more about these "overrides" if there is any such material?
The arrow notation is a lambda function, short-hand for the same Comparator implementation. That's why you see the same results. It is not about #Override here, what you're asking for is how a Comparator works really.
A comparator orders 2 objects in the following order:
negative, sorts descending
zero, does nothing
pozitive, sorts ascending
So for the priority queue part, when the comparator sorts 1, 4, 6, 3, it compares the elements of the array and it swaps them if the difference is negative, e.g. it would swap 1 and 4, 4 and 6, etc.
For the first part of the question, you're using this implementation:
(n2[0] == n1[0])? n1[1] - n2[1]: n2[0] - n1[0]
For 2-sized integer arrays, you're comparing the arrays as following.
If the first element of each array are not equal, you're trying to sort in descending order(i.e. bringing a [7, 0] ahead of a [4, 4])
If the first element of each array is equal, you're trying to sort in
ascending order(i.e. bringing [7,0] ahead of [7,1]).
The javadoc of Comparator method compare(T o1, T o2) says:
Returns:
a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
"Less than" and "greater than" refers to the sort order, not the numerical value, so if you want to sort descending, then 7 is "less than" 5.
So, the following shows how to sort ascending and descending:
// Ascending
(a, b) -> a - b
// Decending
(a, b) -> b - a
However, you should never use - minus operator for this, since it can cause overflow. Use the Integer.compare(int x, int y) method instead, or the equivalent methods on Long, Short, Byte, Double, and Float:
// Ascending
(a, b) -> Integer.compare(a, b)
// Decending
(a, b) -> Integer.compare(b, a)
Next part is that your code sorts by 2 fields, first field descending, second field ascending:
(o1, o2) -> {
if (o1.a != o2.a)
return Integer.compare(o2.a, o1.a); // sort by a (descending)
return Integer.compare(o1.b, o2.b); // secondary sort by b (ascending)
}
Which in your code is done using a ternary operator:
(o1, o2) -> o1.a != o2.a ? Integer.compare(o2.a, o1.a) : Integer.compare(o1.b, o2.b)
Or rather the other way around:
(o1, o2) -> o1.a == o2.a ? Integer.compare(o1.b, o2.b) : Integer.compare(o2.a, o1.a)
#Override annotation just marks when a method from subclass overrides method from superclass. In your second example you are using something called anonymous inner class, you are basically passing an instance of a class, in your example Comparator which has an abstract method called compare. You are implementing that methods functionality in place, so that you don't have to create a new class which is extending Comparator class and so on.
Your first example is basically the same as second, but its syntax is shorter to write and looks cleaner, and easier to read. The second one is called Lambda expression.
Above Arrays.sort accept 2 params. The first is the list need to be sorted, the second is the Comparator. The Comparator is the functional interface that have method compare. Since it's the interface, so you need to implement it (#Override means that your implementation is overriding this method).
The method compare allow you to decide the sort strategy (ascending, descending, blabla).
It's a big leap, but you should learn sooner or later about interfaces, anonymous classes and lambda's.
There's a set of tutorials on the oracle website:
https://docs.oracle.com/javase/tutorial/java/IandI/index.html
https://docs.oracle.com/javase/tutorial/java/javaOO/index.html
From the Comparator.compareTo javadoc https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#compare-T-T-
Compares its two arguments for order. Returns a negative integer,
zero, or a positive integer as the first argument is less than, equal
to, or greater than the second.
So, what you see is expected. If we take two arguments from your input:
[7, 0], [4, 4]
the evaluation would be:
n2[0] - n1[0]
4 - 7 = -3 ( a negative integer)
It sees the first argument [7,0] is less than second argument [4,4] hence it is placed before in order.

Sort array 2D by index Java

I am working on 2D array and I need to organize my array.
Explanation, my array is an Array String and it's containing in the header some double value, and I want to sort the array in function of the header.
The first thing I thought was to get the header and sort the header, get all the columns of the array in another array and comparate the value of the header ordered to each column index[0] and push to another array.
However, I thought there is a way to do it easily, but I don't know if there is, I saw the possibility to sort directly in the array and organize in function of this but I have no idea how to it.
Just in case :
Original array
String[][]myArray = {{3,Toto,30},{2,Tata,29},{1,Titi,13}};
Array expected
String[][]newArray = {{1,Titi,13},{2,Tata,29},{3,Toto,30}};
Open to proposal!
Thanks.
Edit : The header could be Double value.
If I understood you correctly, seems like this:
Arrays.stream(myArray)
.sorted(Comparator.comparingDouble(x -> Double.valueOf(x[0])))
.toArray(String[][]::new);
Of course you can do that in place too, via Arrays::sort and that Comparator
Previous answer just returned the original array when I tried it. Here's what worked for me:
String[][] myArray = {{"3","Toto","30"},{"2","Tata","29"},{"1","Titi","13"}};
Arrays.sort(myArray, new Comparator<String[]>() {
public int compare(String[] lhs, String[] rhs) {
try {
double lhs_value = Double.parseDouble(lhs[0]);
double rhs_value = Double.parseDouble(rhs[0]);
if (lhs_value < rhs_value) return -1;
if (lhs_value > rhs_value) return 1;
return 0; //else the two are equal
} catch (NumberFormatException e) {
//handle exception
return 0;
}
}
});
My output:
1 Titi 13
2 Tata 29
3 Toto 30
Here Arrays.sort (see Javadoc) takes in two parameters: an array you're sorting (myArray), and a Comparator (see Javadoc), which is an interface that allows comparison between two arbitrary types. Since {"3", "Toto", "30"} isn't a type you created, and is just a String[], we're going to make a Comparator<String[]> inline.
Comparators implement a function "compare" which takes in two elements, and returns -1, 0, or 1 to determine the ordering of the elements. Essentially "compare" gets called multiple times in the sorting process to precisely determine sorted order. Here's some pseudocode:
public int compare(Object a, Object b)
if (a comes "before" b) return -1
if (a is "equal" to b) return 0
if (a comes "after" b) return 1
If that isn't clear, you can learn more about the Comparator interface here:
https://www.geeksforgeeks.org/comparator-interface-java/
I know I used "before" and "after" earlier, which are a little fuzzy and non-rigorous. If you're more mathematically inclined, here's a good discussion of the Comparator interface:
https://math.stackexchange.com/questions/1400655/java-comparator-documentation-confused-about-the-terminology-total-order

Stream sorted method with comparator

I don't understand why code1 works but code2 doesn't compile. Please explain.
//Code1:
Stream<String> s = Stream.of("AA", "BB");
s.sorted(Comparator.reverseOrder())
.forEach(System.out::print);
//Code2:
Stream<String> s = Stream.of("AA", "BB");
s.sorted(Comparator::reverseOrder)
.forEach(System.out::print);
The difference between the two is code1 uses Comparator.reverseOrder() while code2 uses Comparator::reverseOrder
Because the first example is a factory-method so when you inspect it, you see that you get a comparator back.
But the second one is a method-reference which you could write like this:
Stream<String> s = Stream.of("AA", "BB");
s.sorted(() -> Comparator.reverseOrder()) // no semantic difference!
.forEach(System.out::print);
But it has a whole different meaning because this time you are given Stream#sorted() a Supplier<Comparator<?>> but it just needs a Comparator<?>
Small Sidenote: Don't store streams in variables, use them directly. So i would suggest you just write:
Stream.of("AA", "BB")
.sorted(Comparator.reverseOrder())
.forEach(System.out::print);
The error message from the compiler should tell you that.
sorted() expects a Comparator instance. Comparator.reverseOrder()returns a Comparator instance. So that works fine.
Comparator::reverseOrder is a method reference to the reverseOrder() method of Comparator. So your code basically says: each time you need to compare two strings, pass them as argument to Comparator.reverseOrder to compare them. But that can't possibly work. This method takes nothing as argument, and returns a Comparator. So it doesn't match the signature of a Comparator<String>, which is supposed to take two Strings as argument, and return an integer.
If you had a method such as
class Foo
public static int compareStrings(String s1, String s2) {
...
}
}
Then you could use
sorted((s1, s2) -> Foo.compareStrings(s1, s2))
which you can transform, using a method reference, to
sorted(Foo::compareStrings)
Because compareStrings, just like the unique abstract method of Comparator<String>, takes two Strings as argument and returns an int.

Check if int is in list of ints in Java (with some constraints)

I'm looking for a simple and elegant one line way to check if the int result of an expression is contained in a list of integers.
Example: Check if foo(…) produces either 1, 2 or 7.
Constraints:
Do not use external libraries, only the JRE.
Java 8 can be assumed.
Solution does not involve (auto)boxing. I personally don't like boxing :-P
foo(…) should only be evaluated once.
No additional method should be required.
No mutable variables should be required.
One or two final variables may be used, including lambdas. But foo(…) (and its result) and the list of ints should not be part of this variables.
The list of integers to check against is known at compile time and may be up to 5 numbers.
To illustrate: java.util.Arrays.asList(1, 2, 7).contains(foo(…)) meets all criteria except it requires boxing.
Note: I'm only interested what possible solutions could look like. In practice I would probably use my illustration example, ArrayUtils.contains(…) from Commons Lang or Ints.contains(…) from Guava.
I'll add three possible solutions later if they don't come up anyway. But I wonder if there are any more elegant solutions, as mine are not really that elegant and I'm a bit surprised Java does not seem to provide something like this out of the box.
How about binary search?
Arrays.binarySearch(new int[] {1, 2, 7}, foo(...)) >= 0
You could filter out the results using a lambda instead, if you're guaranteed a List. If you're not, you can wrap it inside of a Arrays#asList operation.
This will short-circuit on the first value that evaluates to true.
List<Integer> intList = Arrays.asList(foo());
boolean exists = intList.stream().anyMatch(x -> x == 1 || x == 2 || x == 7);
There is going to be a level of boxing due to the way that the stream has to be created; if you really want to avoid it for whatever reason, you've got a couple of options.
Create an int[], then create an IntStream from it:
boolean exists = Arrays.stream(new int[](foo()))
.anyMatch(x -> x == 1 || x == 2 || x == 7);
Convert to an IntStream as an intermediate step (does involve boxing to a degree)
boolean exists = intList.stream()
.mapToInt(Integer::intValue)
.anyMatch(x -> x == 1 || x == 2 || x == 7);
As promised in the question, here are my solutions:
Besides the one of Tagir Valeev using Arrays.binarySearch(…) I did some variations using lambdas:
Building an IntPredicate by joining multiple predicates:
final IntFunction<IntPredicate> intEquals = v1 -> v2 -> v1 == v2;
intEquals.apply(1).or(intEquals.apply(2)).or(intEquals.apply(7)).test(foo(…));
This makes adding a new number to the list very verbose.
First building a IntPredicate avoids the need to encapsulate the result of foo(…) somehow to prevent evaluating it more than once.
This one does the same as the previous solution, but adding a new number is less verbose (at the cost of a more complex lambda):
final IntFunction<IntPredicate> intEquals = v1 -> v2 -> v1 == v2;
final Function<Function<IntStream.Builder, IntStream.Builder>, IntPredicate> pred =
c -> c.apply(IntStream.builder()).build().mapToObj(intEquals).reduce(IntPredicate::or).get();
pred.apply(b -> b.add(1).add(2).add(7)).test(foo(…));
Building a IntPredicate from an array:
final Function<int[], IntPredicate> oneOf =
ints -> testInt -> IntStream.of(ints).anyMatch(arrayInt -> testInt == arrayInt);
oneOf.apply(new int[] { 1, 2, 7 }).test(foo(…));
This is in principe similar to the answer provided by Makoto but the other way around (he put the single value in front of the stream).
Using IntStream.of(…) instead of writing new int[] {…}:
final Function<IntStream, IntPredicate> oneOf =
ints -> testInt -> ints.anyMatch(streamInt -> testInt == streamInt);
oneOf.apply(IntStream.of(1, 2, 7)).test(foo(…));
This could be even shortened using a static import of IntStream.of.
With the use of BiPredicate the call can be made even shorter:
final IntFunction<IntPredicate> intEquals = v1 -> v2 -> v1 == v2;
final BiPredicate<IntStream, IntSupplier> oneOf =
(list, value) -> list.anyMatch(intEquals.apply(value.getAsInt()));
oneOf.test(of(1, 2, 7), () -> foo(…));
Here a IntSupplier is used to avoid the recalculation of foo(…).
So the bottom line for me is: Java does not provide a concise, ready to use function that meets the requirements. The binarySearch method works but requires a sorted list of integers and does not express the intend very well. The lambda variants are all a bit complex. So apparently I'll have to drop some of the constraints :-P
But it was an interesting challenge to look at, in my opinion…

Sorting Strings using Collections.sort?

I'm attempting to sort two strings using the collection.sort() method, but I'm having issues understanding the logic of the implementation. Here is what I have so far. Are there any issues with my implementation?
Note: I want to sort them alphabetically:
"Apple" > "Orange"
Collections.sort(mailbox.getMessages() , (String a, String b) -> {
if (String.valueOf(a.charAt(0)) > String.valueOf(b.charAt(0))) {
return -1;
}
else if (String.valueOf(a.charAt(0)) <
String.valueOf(b.charAt(0))) {
return 1;
}
else {
return 0;
}
});
String implements a Comparable<String> which is implemented as a lexicographical comparison, in other words, by default "Apple".compareTo("Orange") < 0. So the default is sufficient.
Now Collections.sort has a variant that takes this comparator into account, you can thus simply use:
Collections.sort(mailbox.getMessages());
About your own implementation:
You shouldn't use String.valueof to cast back to a string: you can compare chars with the <, but you can't use this operator on Strings. Furthermore your implementation is not recursive: if the two first characters are equal, that doesn't mean the String's are equal per se, for instance "Apple" and "Ambiguous". So you would have to implement a more complex comparator.
You can't compare String with the symbol >. You can simply do :
Collections.sort(mailbox.getMessages(), (String a, String b) -> {
return Character.compare(a.charAt(0), b.charAt(0));
});
Note that this will sort according only to first character. If you want to sort on the entire string value lexographically then you can simply use Collections.sort(mailbox.getMessages()) as String already implements Comparable.

Categories