How to define comparator on SortedSet<> like TreeSet<>? - java

I want to make a lexical sorted list of strings, so i went with the basic SortedSet
1) Set<String> words = new SortedSet<String>(){}
and realized that SortedSet is an abstract class, in which I will have to implement the comapartor method. So i went and searched on google and found out that treeSet is better and i can use its predefined comparator method.
2) SortedSet<String> words = new TreeSet<String>(){}
When went to java docs i realized that TreeSet extends AbstractSet rather than SortedSet. Question 1 - Can anyone explain how the 2nd line is still working(like i am not generalizing the Set which i would normally do instead i am using two totally different Classes with no Parent child relation).
Question 2 - how to define comparator of SortedSet which will work as TreeSet. So here is the working code with TreeSet
SortedSet<String> words = new TreeSet<>();
Scanner scanner1 = new Scanner(System.in);
String s1 = scanner1.nextLine();
int a = scanner1.nextInt();
while(s1.length()>a){
words.add(s1.substring(0,a));
s1 = s1.substring(a);
}
Iterator itr = words.iterator();
while(itr!= null&&itr.hasNext()){
System.out.println(itr.next());
}
Normal Input
welcometojava
3
Expected Output
com
eto
jav
wel
Edit-1
For the answer of Question 2, i am expecting something like this
Set<String> words = new SortedSet<String>() {
#Override
public Comparator<? super String> comparator() {
return null;
}
......
I basically want to learn, how to make a basic comparator "like" in TreeSet while using SortedSet? I understand that if there is natural ordering i don't need to define a new comparator.

Answer 1:
TreeSet<T> implements the NavigableSet<T> interface, which extends SortedSet<T> who also extends Set<T>.
The interfaces themselves doesn't actually do the sorting, the concrete class does.
So:
Set<String> myStrings = new TreeSet<>();
// Add a bunch of strings
// ...
for (String s : myStrings) {
System.out.println(s);
}
You would still have them in sorted order.
Answer 2:
Firstly, for classes that already implement Comparable<T>, you can omit the Comparator for the TreeSet, as "Natural Ordering" is meant by using the Comparable<T>'s compareTo method.
Otherwise you can supply a Comparator instance to as the TreeSet constructor's first argument:
Set<String> myStrings = new TreeSet<>(new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
// Define comparing logic here
return o1.compareTo(o2);
}
});
or use Java 8 Lambdas:
Set<String> myStrings = new TreeSet<>((o1, o2) -> o1.compareTo(o2));

To answer your question,
TreeSet also implements NavigableSet which extends SortedSet
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable
public interface NavigableSet<E> extends SortedSet<E>
By default sort will be done based on natural ordering and the basic primitives wrappers (Integer, Long, ...) implements Comparable interface, so no need to implement the Comparable if the collection holds wrappers of Primitives and natural ordering is expected
But your custom class should implement Comparable if it should be ordered in TreeSet else ClassCastException will be thrown once you add the second element

TreeSet implements NavigableSet, which in turn extends SortedSet, which is why line 2) works. See TreeSet Java doc.
For the second part, try this:
SortedSet<String> ts = new TreeSet<String>(new TSComparator());
class TSComparator implements Comparator<String>{
#Override
public int compare(String str1, String str2) {
return str1.compareTo(str2);
}
}

SortedSet<T> is not an abstract class, it is an interface.
TreeSet<T> does implement SortedSet<T>, but not directly: the chain of inheritance goes as follows:
Set<T> - SortedSet<T> - NavigableSet<T>
That is why the assignment SortedSet<String> words = new TreeSet<String>() works: TreeSet<T> is a NavigableSet<T>, and NavigableSet<T> is a SortedSet<T>, so the assignment is legal.
When you do not supply an explicit comparator, TreeSet<T>'s uses natural ordering supplied by T's implementation of Comparable<T>.

Related

Java:Sorting an arraylist having linkedlist with the first element

Consider this arraylist with linkedlist objects
(are->1->3->7 , croco-> 4 ->1 , bat ->3->8).
Now I have to sort the arraylist in java in ascending order considering the first element of linkedlist contained in arraylist.
There is a method Collections.sort() but it is useful when objects are strings.
What to apply in this situation?
The final list should look something like this
(are->1->3->7 , bat ->3->8 , croco-> 4 ->1 )
Collections.sort(); has an overloaded method that takes a comparator as well. So you want something like this:
Collections.sort(lists, (o1, o2) ->
o1.get(0).compareTo(o2.get(0))
);
Or if you can use Java 8, there is a default sort method in List interface:
lists.sort(Comparator.comparing(o -> o.get(0)));
Without much context, I might be wrong. However, from my judgement if you're using your own objects which contain an identifier (are, croco and bat), and a linkedlist(numbers), then you can use the following solution.
Your class needs to implement the Comparable Interface
You need to override the compareTo() method
Collections.sort() should sort your objects in ascending order.
public class Example implements Comparable {
private String identifier;
private List<Integer> list;
// constructors
// getters
#Override
public int compareTo(Object other){
if(this.list.head() > other.list.head()) return 1;
if(this.list.head() < other.list.head()) return -1;
else return 0;
}
}

How to build Priority Queue with customized comparator in linear time

In the constructor of PriorityQueue, we can pass in a collection like List or Set, which builds the PriorityQueue in linear time.
However, this also means the PriorityQueue will use a default Comparator.
I want to use my own comparator, so I can have something else other than a min heap.
The only way I can think of is to wrap the collection in a SortedSet and put a customized comparator in it.
Is there any other good way to do this?
Assume you have class A (or a pojo)
with an int priority; field which holds your priority for this object and its getter getPriority()
then you have it something like this:
Queue<A> queue = new PriorityQueue<>(
4 //initialCapacity
, new Comparator<A>() {
public int compare(A p1, A p2) {
return Integer.valueOf(p1.getPriority()).compareTo(p2.getPriority());
}
});
Create proxy class that contains your data object and implements Comparable interface. Create list of such objects, pass it to PriorityQueue constructor.
I don't know of effective SortedSet implementations with garanteed creation time of O(n) for comparable objects. It is possible to sort array in O(n) for radix-friendly key though (in reality linear sort tends to be not-so-fast in general case), so you can make customized SortedSet with fast creation compatible to your special comparators.
Heap constructor for comparable objects can do it in O(n) only because it does not fully sort the list.
In the constructor of PriorityQueue, we can pass in a collection like List or Set, which builds the PriorityQueue in linear time.
Wrong.
However, this also means the PriorityQueue will use a default Comparator.
Wrong.
The javadoc says
If the specified collection is an instance of a SortedSet or is another PriorityQueue, this priority queue will be ordered according to the same ordering.
So when starting from a recognized sorted collection, you get its Comparator. Moreover, you get linear time.
Otherwise, you don't. The source shows it rather clearly (look for heapify()).
If you have an unsorted list, there's no way to obtain a priority queue in linear time (unless the priority queue is ensuring the heap property lazily; but that's cheating).
I have the same problem.
The only thing that I think is create a wrapper class that contains an object T and implements Comparable interface like this:
class ModifiedPriorityQueue<T> extends PriorityQueue<Wrapper<T>> {
public ModifiedPriorityQueue(Collection<T> collection, Comparator<T> comparator) {
super(collection.stream().map(x -> new Wrapper<>(x, comparator)).collect(Collectors.toList()));
}
}
class Wrapper<T> implements Comparable<Wrapper<T>> {
private final T object;
private Comparator<T> comparator;
public Wrapper(T object, Comparator<T> comparator) {
this.object = object;
this.comparator = comparator;
}
#Override
public int compareTo(Wrapper<T> o) {
return comparator.compare(object, o.object);
}
#Override
public String toString() {
return object.toString();
}
}
class Main {
public static void main(String[] args) {
Collection<Integer> elements = Arrays.asList(1, 2, 3, 4);
ModifiedPriorityQueue<Integer> p = new ModifiedPriorityQueue<>(elements, Comparator.reverseOrder());
while (!p.isEmpty()) {
System.out.println(p.poll());
}
}
}

Java Sort with Comparable

I have an ArrayList of Person objects. A Person has name, age and height. My goal is to sort this ArrayList<Person>. I have implemented Comparable<Person> and have defined compareTo() but when I try to sort it, it give me this error:
The method sort(Comparator) in the type ArrayList is not applicable for the argument ()"
The way I understand is that if you implement Comparable and then define compareTo everything else is magically done for you.
Can some one explain how to this works and why I am getting this error?
My guess is that your code looks like this:
ArrayList<Person> people = ...;
people.sort();
Look at the JavaDoc for ArrayList. Do you see a method public void sort() (with no parameters)? No - there is no such method.
That is the meaning of the error: The method sort(Comparator) in the type ArrayList is not applicable for the argument () -- There is a method sort(Comparator), but you have not supplied parameters that match it.
Assuming Person implements Comparable (and therefore has a compareTo() method), you can use Collections.sort(), which sorts arbitrary List<Comparable>
Collections.sort(people);
This is because Collections has a static method:
static <T extends Comparable<? super T>> void sort(List<T> list);
(it also has a sort(List<T> list, Comparator<T> comparator))
... or you can pass a comparator to List.sort(), which is quite easy with Java 8 lambdas:
people.sort((a,b) -> a.compareTo(b));
(Or, if you prefer the old style):
people.sort(new Comparator<String>() {
#Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
(Actually as of Java 8, this comparator is provided by the standard library, as Comparator.naturalOrder())
The point of comparators is that you can sort according to different criteria. For example:
people.sort((a,b) -> a.lastName().compareTo(b.lastName()));
people.sort((a,b) -> a.lastName().compareToIgnoreCase(b.lastName()));
people.sort((a,b) -> Integer.compare(a.age(),b.age()));
// etc.
... or using methods in Comparator:
people.sort(Comparator.comparing(Person::lastName));
people.sort(Comparator.comparing(Person::lastName)
.thenComparing(Person::firstName));
Either you use a structure which uses the Comparable interface to order its elements when you add a new element inside it :
TreeSet<Person> persons = new TreeSet<>();
Person personOne = ...
Person personTwo = ...
persons.add(personOne);
persons.add(personTwo);
Either you use a List and the Collections.sort(List<T> list) method which takes as argument the list you want to sort (there is an overload of this method but it is not relevant in your case):
List<Person> persons = new ArrayList<>();
Person personOne = ...
Person personTwo = ...
persons.add(personOne);
persons.add(personTwo);
Collections.sort(persons);
With the TreeSet, the elements are sorted as soon as added and with the List, the elements are not sorted when you add them. Only, the call to the Collections.sort() method sorts the list.

How will sort method do a sort, on a list that contains objects of type Foo?

How will a LinkedList of type :
LinkedList<FooClass> list = new LinkedList<FooClass>();
// FooClass extends Comparable
list.add(foo_obj_1);
list.add(foo_obj_2);
list.add(foo_obj_3);
list.add(foo_obj_4);
be sorted by :
Collection.sort(list);
What effect will sort method have on list that contains a list of objects.On what basis will the list be sorted ?
If you make you an object comparable, then you override the compareTo method. You can then write your own custom criteria based on what fields you want to compare. A sort can then be performed as usual.
class Author implements Comparable<Author>{
String firstName;
String lastName;
#Override
public int compareTo(Author other){
int last = this.lastName.compareTo(other.lastName);
return last == 0 ? this.firstName.compareTo(other.firstName) : last;
}
}
If your object does not implement Comparable then you will not be able to perform any of the various sort implementations.
Check out the API of Collections#sort(List<T>). It will only compile if T extends Comparable.
If Foo does not extend Comparable, your code will not compile.
If it does, the list will be sorted by Foo's natural ordering, as explained here.

How do I sort a Set to a List in Java?

In Java, I have a Set, and I want to turn it into a sorted List. Is there a method in the java.util.Collections package that will do this for me?
The answer provided by the OP is not the best. It is inefficient, as it creates a new List and an unnecessary new array. Also, it raises "unchecked" warnings because of the type safety issues around generic arrays.
Instead, use something like this:
public static
<T extends Comparable<? super T>> List<T> asSortedList(Collection<T> c) {
List<T> list = new ArrayList<T>(c);
java.util.Collections.sort(list);
return list;
}
Here's a usage example:
Map<Integer, String> map = new HashMap<Integer, String>();
/* Add entries to the map. */
...
/* Now get a sorted list of the *values* in the map. */
Collection<String> unsorted = map.values();
List<String> sorted = Util.asSortedList(unsorted);
Sorted set:
return new TreeSet(setIWantSorted);
or:
return new ArrayList(new TreeSet(setIWantSorted));
Here's how you can do it with Java 8's Streams:
mySet.stream().sorted().collect(Collectors.toList());
or with a custom comparator:
mySet.stream().sorted(myComparator).collect(Collectors.toList());
List myList = new ArrayList(collection);
Collections.sort(myList);
… should do the trick however. Add flavour with Generics where applicable.
Always safe to use either Comparator or Comparable interface to provide sorting implementation (if the object is not a String or Wrapper classes for primitive data types) .
As an example for a comparator implementation to sort employees based on name
List<Employees> empList = new LinkedList<Employees>(EmpSet);
class EmployeeComparator implements Comparator<Employee> {
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
}
Collections.sort(empList , new EmployeeComparator ());
Comparator is useful when you need to have different sorting algorithm on same object (Say emp name, emp salary, etc). Single mode sorting can be implemented by using Comparable interface in to the required object.
There's no single method to do that. Use this:
#SuppressWarnings("unchecked")
public static <T extends Comparable> List<T> asSortedList(Collection<T> collection) {
T[] array = collection.toArray(
(T[])new Comparable[collection.size()]);
Arrays.sort(array);
return Arrays.asList(array);
}
You can convert a set into an ArrayList, where you can sort the ArrayList using Collections.sort(List).
Here is the code:
keySet = (Set) map.keySet();
ArrayList list = new ArrayList(keySet);
Collections.sort(list);
TreeSet sortedset = new TreeSet();
sortedset.addAll(originalset);
list.addAll(sortedset);
where originalset = unsorted set and list = the list to be returned
#Jeremy Stein I wanted to implement same code. As well I wanted to sort the set to list, So instead of using Set I converted set values into List and sort that list by it's one the variable.
This code helped me,
set.stream().sorted(Comparator.comparing(ModelClassName::sortingVariableName)).collect(Collectors.toList());
I am using this code, which I find more practical than the accepted answer above:
List<Thing> thingList = new ArrayList<>(thingSet);
thingList.sort((thing1, thing2) -> thing1.getName().compareToIgnoreCase(thing2.getName()));

Categories