Sorting a list of Generics in Java? - java

I'm trying to implement a sorting for Generics in Java.
Here is the abstract class Function (T is my "key" in order to sort):
public abstract class Function<T extends Comparable<T>, S> {
abstract public T compute(S o);
}
Here is class Applier, whose method "apply" sorts the list according on the result of "compute":
import java.util.ArrayList;
import java.util.Iterator;
public class Applier<T extends Comparable<T>, S> {
ArrayList<S> apply(ArrayList<S> input, Function<T, S> function) {
ArrayList<T> output = new ArrayList<>();
for(Iterator<S> it = input.iterator(); it.hasNext(); ){
output.add(function.compute(it.next()));
}
T tmpTi, tmpTj;
S tmpSi, tmpSj;
for(int i=0; i<input.size(); i++) {
for(int j=i+1; j<input.size(); j++) {
if(output.get(i).compareTo(output.get(j))>0) {
tmpTi = output.get(i);
tmpTj = output.get(j);
output.remove(j);
output.remove(i);
output.add(i, tmpTi);
output.add(i, tmpTj);
tmpSi = input.get(i);
tmpSj = input.get(j);
input.remove(j);
input.remove(i);
input.add(i, tmpSj);
input.add(j, tmpSi);
}
}
}
return input;
}
}
My question is: is there a smarter way to do this sorting, maybe not with a bubblesort?
Here is also the main class:
public static void main(String[] args) {
Applier a = new Applier<>();
StringLength strlen = new StringLength();
ArrayList<String> array = new ArrayList<>();
array.add("Hola");
array.add("Man");
array.add("randomstufff");
array.add("Zerrone");
array.add("Info3a");
System.out.println("Order by length");
System.out.print("before: ");
System.out.println(array);
a.apply(array, strlen); //works on original object
System.out.print("After: ");
System.out.println(array);

Basically you want to sort an array based on some other array. You will be able to use Collections.sort if you introduce a wrapper object that contains both the values and the function results, and sort that one.
Here's a solution using Java 8 streaming API:
public class Applier<T extends Comparable<T>, S> {
static class Wrapper<T extends Comparable<T>,S> implements Comparable<Wrapper<T,S>> {
T key;
S value;
Wrapper(S s, Function<T, S> function) {
this.key = function.compute(s);
this.value = s;
}
public int compareTo(Wrapper<T,S> that) {
return key.compareTo(that.key);
}
}
ArrayList<S> apply(ArrayList<S> input, Function<T, S> function) {
S[] sorted = (S[]) IntStream.range(0, input.size())
.mapToObj(i -> new Wrapper<T,S>(input.get(i), function))
.sorted()
.map(b -> b.value).toArray();
input.clear();
input.addAll(Arrays.asList(sorted));
return input;
}
}

Note that there's an error in the way you swap elements in your Bubble Sort: When re-inserting the elements into output, you misplaced i and j. Also, instead of removing and re-inserting the elements, just use set(index, element) to overwrite the previous entry.
Also, instead of using two lists and keeping those lists in synch, better just use a Map.
public static class Applier<T extends Comparable<T>, S> {
ArrayList<S> apply(ArrayList<S> input, Function<T, S> function) {
Map<S, T> compareBy = new HashMap<>();
for (S s : input) {
compareBy.put(s, function.compute(s));
}
for(int i=0; i<input.size(); i++) {
for(int j=i+1; j<input.size(); j++) {
if (compareBy.get(input.get(i)).compareTo(compareBy.get(input.get(j))) > 0) {
S tmpS = input.get(j);
input.set(j, input.get(i));
input.set(i, tmpS);
}
}
}
return input;
}
}
And of course, sorting is already implemented in Java. So other than for learning how to code, you should always use the builtin functions. In Java 8, it's just a single line:
Collections.sort(array, Comparator.comparing(String::length));
Note, however, that Comparator.comparing will call the comparator function for each pairwise comparison (i.e. on the order of 2nlogn times for a decent sorting algorithm). If that function is computationally very expensive, you might want to cache it yourself, using a Map.
Map<String, Integer> compareBy = array.stream().collect(Collectors.toMap(s -> s, s -> s.length()));
Collections.sort(array, Comparator.comparing((String s) -> compareBy.get(s)));

Related

Java 8 Lambda Expressions

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

How to use Collections.sort() in Java?

I got an object Recipe that implements Comparable<Recipe> :
public int compareTo(Recipe otherRecipe) {
return this.inputRecipeName.compareTo(otherRecipe.inputRecipeName);
}
I've done that so I'm able to sort the List alphabetically in the following method:
public static Collection<Recipe> getRecipes(){
List<Recipe> recipes = new ArrayList<Recipe>(RECIPE_MAP.values());
Collections.sort(recipes);
return recipes;
}
But now, in a different method, lets call it getRecipesSort(), I want to sort the same list but numerically, comparing a variable that contains their ID. To make things worse, the ID field is of the type String.
How do I use Collections.sort() to perform the sorts in Java?
Use this method Collections.sort(List,Comparator) . Implement a Comparator and pass it to Collections.sort().
class RecipeCompare implements Comparator<Recipe> {
#Override
public int compare(Recipe o1, Recipe o2) {
// write comparison logic here like below , it's just a sample
return o1.getID().compareTo(o2.getID());
}
}
Then use the Comparator as
Collections.sort(recipes,new RecipeCompare());
The answer given by NINCOMPOOP can be made simpler using Lambda Expressions:
Collections.sort(recipes, (Recipe r1, Recipe r2) ->
r1.getID().compareTo(r2.getID()));
Also introduced after Java 8 is the comparator construction methods in the Comparator interface. Using these, one can further reduce this to 1:
recipes.sort(comparingInt(Recipe::getId));
1 Bloch, J. Effective Java (3rd Edition). 2018. Item 42, p. 194.
Create a comparator which accepts the compare mode in its constructor and pass different modes for different scenarios based on your requirement
public class RecipeComparator implements Comparator<Recipe> {
public static final int COMPARE_BY_ID = 0;
public static final int COMPARE_BY_NAME = 1;
private int compare_mode = COMPARE_BY_NAME;
public RecipeComparator() {
}
public RecipeComparator(int compare_mode) {
this.compare_mode = compare_mode;
}
#Override
public int compare(Recipe o1, Recipe o2) {
switch (compare_mode) {
case COMPARE_BY_ID:
return o1.getId().compareTo(o2.getId());
default:
return o1.getInputRecipeName().compareTo(o2.getInputRecipeName());
}
}
}
Actually for numbers you need to handle them separately check below
public static void main(String[] args) {
String string1 = "1";
String string2 = "2";
String string11 = "11";
System.out.println(string1.compareTo(string2));
System.out.println(string2.compareTo(string11));// expected -1 returns 1
// to compare numbers you actually need to do something like this
int number2 = Integer.valueOf(string1);
int number11 = Integer.valueOf(string11);
int compareTo = number2 > number11 ? 1 : (number2 < number11 ? -1 : 0) ;
System.out.println(compareTo);// prints -1
}
Use the method that accepts a Comparator when you want to sort in something other than natural order.
Collections.sort(List, Comparator)
Sort the unsorted hashmap in ascending order.
// Sorting the list based on values
Collections.sort(list, new Comparator<Entry<String, Integer>>() {
public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2)
{
return o2.getValue().compareTo(o1.getValue());
}
});
// Maintaining insertion order with the help of LinkedList
Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
for (Entry<String, Integer> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}

junit testing equality of an Iterable

I am trying to write unit tests for a BinarySearchTree class
The keys() return an Iterable.It uses another class called Queue in which the keys are enqueued and returned..
The Queue (third party class) however doesn't have any equals() defined.
public class BinarySearchTree<Key extends Comparable<Key>,Value> {
Node root ;
private class Node{
private Key key;
private Value val;
private Node left;
private Node right;
private int N;
public Node(Key k, Value v,int N) {
super();
this.key = k;
this.val = v;
this.N = N;
}
}
public Iterable<Key> keys(){
Queue<Key> q = new Queue<Key>();
inOrder(root,q);
return q;
}
private void inOrder(Node x,Queue q){
if(x == null)return;
inOrder(x.left,q);
q.enqueue(x.key);
inOrder(x.right,q);
}
...
}
trying to write unit test
#Test
public void testKeys(){
MyBST<String, Integer> st = new MyBST<String, Integer>();
st.put("S",7);
st.put("E",2);
st.put("X",8);
st.put("A",3);
st.put("R",4);
st.put("C",1);
st.put("H",5);
st.put("M",6);
Queue<String> q = new Queue<String>();
q.enqueue("A");
q.enqueue("C");
q.enqueue("E");
q.enqueue("H");
q.enqueue("M");
q.enqueue("R");
q.enqueue("S");
q.enqueue("X");
Iterable<String> actual = st.keys();
assertEquals(q,actual);
}
This fails
java.lang.AssertionError: expected: std.Queue<A C E H M R S X > but was: std.Queue<A C E H M R S X >
at org.junit.Assert.fail(Assert.java:93)
at org.junit.Assert.failNotEquals(Assert.java:647)
at org.junit.Assert.assertEquals(Assert.java:128)
at org.junit.Assert.assertEquals(Assert.java:147)
at week4.MyBSTTests.testKeys(BSTTests.java:304)
Do I have to implement an equals() in the third party class or is there any other way to do this to check equality? All I could think of was running a loop dequeueing from queue q and comparing it with what the iterator returned.I am not sure if there is a better way.. Please advise..
Iterable<String> actual = st.keys();
Iterator<String> actualit = actual.iterator();
while(actualit.hasNext()){
String actualkey = actualit.next();
String exp = q.dequeue();
assertEquals(actualkey,exp);
}
Use Hamcrest's Matchers.contains (described here). For example:
assertThat(queue1.keys(), Matchers.contains("A", "C", "E", "H", "M", "R", "S", "X"));
This will check the elements that the Iterable returns without needing equality implemented on your queue class.
you can use the utility class java.util.Arrays.
From what I remember the Queue interface has a toArray method. So it would be something like this:
assertTrue(Arrays.equals(queue1.toArray(),queue2.toArray()));
As it is a third party library, you could use apache commons:
Object[] o = IteratorUtils.toArray(queue1.iterator());
Object[] o2 = IteratorUtils.toArray(queue1.iterator());
assertTrue(Arrays.equals(o,o2));
This is how I did this.
I converted the Iterable to the ArrayList. Then I made another arraylist of expected key values. That way I am able to check if two arrayLists are equal using assertEquals(arrayList1, arrayList2).
Here is the code I wrote for testing my preOrder traversal method.
import static org.junit.Assert.*;
import java.util.ArrayList;
import org.junit.Test;
public class BSTTest
{
BST<Integer, String> binaryTree = new BST<Integer, String>();
#Test
public void preOrdertest()
{
binaryTree.put(87, "Orange");
binaryTree.put(77, "Black");
binaryTree.put(81, "Green");
binaryTree.put(89, "Blue");
binaryTree.put(4, "Yellow");
binaryTree.put(26, "white");
binaryTree.put(23, "Purple");
binaryTree.put(27, "Violet");
binaryTree.put(57, "red");
binaryTree.put(1, "crimson");
ArrayList<Integer> testList = new ArrayList<>();
testList.add(87);
testList.add(77);
testList.add(4);
testList.add(1);
testList.add(26);
testList.add(23);
testList.add(27);
testList.add(57);
testList.add(81);
testList.add(89);
Iterable<Integer> actual = binaryTree.preOrder();
ArrayList<Integer> actualList = new ArrayList<>();
if (actual != null)
{
for (Integer e : actual)
actualList.add(e);
}
assertEquals(testList, actualList);
}
}

Java - Sorting Grouped by Ties

I'm writing a Java program in which I want to sort a set of items and get the N-highest elements of the set. The thing is, though, that I want the elements to be returned grouped by their rank -- so if I want the 3 highest elements, but there is a tie between two elements for third place, then the third result is a collection that contains the two tied elements.
I know I could write this myself, but I'm wondering if it's already been implemented somewhere else. Does anybody know of anything like this?
Sounds like the Google Collection's MultiMap might be what you're after.
Use the "rank" as your key when inserting your elements. Then sort the keys.
This is what I ended up going with:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.google.common.collect.Ordering;
public final class Sorting {
private Sorting() {}
public static <T extends Comparable<? super T>> List<List<T>> rank(
Iterable<T> iterable, int nRanks) {
if (nRanks < 0) {
throw new IllegalArgumentException(Integer.toString(nRanks));
}
if (nRanks == 0) {
return new ArrayList<List<T>>();
}
Iterator<T> iter = Ordering.natural().sortedCopy(iterable).iterator();
List<List<T>> ret = new ArrayList<List<T>>();
if (iter.hasNext()) {
T prev = iter.next();
List<T> group = new ArrayList<T>();
group.add(prev);
ret.add(group);
int rank = 1;
while (iter.hasNext()) {
T next = iter.next();
if (prev.compareTo(next) > 0) {
rank++;
if (rank > nRanks) {
break;
}
group = new ArrayList<T>();
ret.add(group);
}
group.add(next);
prev = next;
}
}
return ret;
}
}

Comparator interface

ok I was going to edit my previous question but i wasnt sure if it was the right way to do it so i'll just give another question about Comparator, now i want to be able to sort with different ways. I have a bank checks and i want to sort with checkNumber then checkAmount
i managed to do it with checkNumber but couldnt figure out how with checkAmount
here is how i did it for checkNumber:
import java.util.Comparator;
public class Check implements Comparator {
private int checkNumber;
private String description;
private double checkAmount;
public Check() {
}
public Check(int newCheckNumber, double newAmountNumber) {
setCheckNumber(newCheckNumber);
setAmountNumber(newAmountNumber);
}
public String toString() {
return checkNumber + "\t\t" + checkAmount;
}
public void setCheckNumber(int checkNumber) {
this.checkNumber = checkNumber;
}
public int getCheckNumber() {
return checkNumber;
}
public void setAmountNumber(double amountNumber) {
this.checkAmount = amountNumber;
}
public double getAmountNumber() {
return checkAmount;
}
#Override
public int compare(Object obj1, Object obj2) {
int value1 = ((Check) obj1).getCheckNumber();
int value2 = ((Check) obj2).getCheckNumber();
int result = 0;
if (value1 > value2){
result = 1;
}
else if(value1 < value2){
result = -1;
}
return result;
}
}
import java.util.ArrayList;
import java.util.Collections;
import test.CheckValue;
public class TestCheck {
public static void main(String[] args) {
ArrayList List = new ArrayList();
List.add(new Check(445, 55.0));
List.add(new Check(101,43.12));
List.add(new Check(110,101.0));
List.add(new Check(553,300.21));
List.add(new Check(123,32.1));
Collections.sort(List, new Check());
System.out.println("Check Number - Check Amount");
for (int i = 0; i < List.size(); i++){
System.out.println(List.get(i));
}
}
}
thank you very much in advance and please tell me if im submiting things in the wrong way.
What you really want to do is define a separate class to act as the Comparator object - don't make your actual Check class the comparator, but instead have 3 classes:
the Check class itself
a CheckAmountComparator class (or something similar) that implements Comparator<Check>
a CheckNumberComparator class (or something similar) that implements Comparator<Check>
Then when you want to sort one way or another, you simply pass an instance of the Comparator-implementing class corresponding to the type of sorting you want to do. For instance, to sort by amount, it'd then become...
Collections.sort(yourListVariable, new CheckAmountComparator());
Also - I'd highly suggest naming your variable something other than List, since List is used as a type name in Java.
You should make Check implements Comparable<Check>, but not itself implements Comparator.
A Comparable type defines the natural ordering for the type, and a Comparator for a type is usually not the type itself, and defines their own custom ordering of that type.
Related questions
When to use Comparable vs Comparator
Java: What is the difference between implementing Comparable and Comparator?
Can I use a Comparator without implementing Comparable?
Also, you shouldn't use raw type. You need to use parameterized generic types, Comparable<Check>, Comparator<Check>, List<Check>, etc.
Related questions
What is a raw type and why shouldn’t we use it?
A String example
Let's take a look at what String has:
public final class String implements Comparable<String>
String defines its natural ordering as case-sensitive
It has a field
public static final Comparator<String> CASE_INSENSITIVE_ORDER
Here we have a case-insensitive custom Comparator<String>
An example of using this is the following:
List<String> list = new ArrayList<String>(
Arrays.asList("A", "B", "C", "aa", "bb", "cc")
);
Collections.sort(list);
System.out.println(list);
// prints "[A, B, C, aa, bb, cc]"
Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
System.out.println(list);
// prints "[A, aa, B, bb, C, cc]"
Here's an example of sorting List<String> using both its natural ordering and your own custom Comparator<String>. Note that we've defined our own Comparator<String> without even changing the final class String itself.
List<String> list = new ArrayList<String>(
Arrays.asList("1", "000000", "22", "100")
);
Collections.sort(list);
System.out.println(list);
// prints "[000000, 1, 100, 22]" natural lexicographical ordering
Comparator<String> lengthComparator = new Comparator<String>() {
#Override public int compare(String s1, String s2) {
return Integer.valueOf(s1.length())
.compareTo(s2.length());
}
};
Collections.sort(list, lengthComparator);
System.out.println(list);
// prints "[1, 22, 100, 000000]" ordered by length
Comparator<String> integerParseComparator = new Comparator<String>() {
#Override public int compare(String s1, String s2) {
return Integer.valueOf(Integer.parseInt(s1))
.compareTo(Integer.parseInt(s2));
}
};
Collections.sort(list, integerParseComparator);
System.out.println(list);
// prints "[000000, 1, 22, 100]" ordered by their values as integers
Conclusion
You can follow the example set by String, and do something like this:
public class Check implements Comparable<Check> {
public static final Comparator<Check> NUMBER_ORDER = ...
public static final Comparator<Check> AMOUNT_ORDER = ...
public static final Comparator<Check> SOMETHING_ELSE_ORDER = ...
}
Then you can sort a List<Check> as follows:
List<Check> checks = ...;
Collections.sort(checks, Check.AMOUNT_ORDER);

Categories

Resources