Sorting ArrayList by multiple properties - java

I have an ArrayListof players with an attribute of username of type string and winratio of type long which is just a ratio of gameswon/gamesplayed*100.
I want to sort the ArrayList by username which I have already done using a Comparator class, however I also want to make another method that sorts the players based winratio and if they have equal winratio to order them in terms of their username. I am not sure on how to combine the two comparators together and give them a hierarchy so it knows which sort to preference over another.
Thanks

Calculate the difference in winRatio, if it is 0, return the difference in name, for example...
public class MultiComparator implements Comparator<Player> {
#Override
public int compare(Player o1, Player o2) {
int result = (int) (o1.getWinRatio() - o2.getWinRatio());
if (result == 0) {
result = o1.getUserName().compareTo(o2.getUserName());
}
return result;
}
}
And because I had nothing to go on, I used this
public interface Player {
public String getUserName();
public long getWinRatio();
}
As the base object
Another (weird) idea might be to create a "chained" Comparator, allowing you to take two (or more) Comparator and chain them together, so that while the result of any one Comparator is 0, it will keep trying to compare the values to the Comparator in the list...
public class RatioComparator implements Comparator<Player> {
#Override
public int compare(Player o1, Player o2) {
return (int) (o1.getWinRatio() - o2.getWinRatio());
}
}
public class NameComparator implements Comparator<Player> {
#Override
public int compare(Player o1, Player o2) {
return o1.getUserName().compareTo(o2.getUserName());
}
}
public class ChainedComparator implements Comparator<Player> {
private Comparator<Player>[] comparators;
public ChainedComparator(Comparator<Player>... comparators) {
this.comparators = comparators;
}
#Override
public int compare(Player o1, Player o2) {
int result = -1;
for (Comparator<Player> proxy : comparators) {
result = proxy.compare(o1, o2);
if (result != 0) {
break;
}
}
return result;
}
}
Which you could use something like...
Collections.sort(list, new ChainedComparator(new RatioComparator(), new NameComparator()));
This is untested and is just a rough idea ;)

What you want can be achieved using below code inside compare method,
int compare(T o1, T o2){
if(o1.winratio < o2.winratio){
return -1;
}else if(o1.winratio < o2.winratio){
return +1;
}else{
return o1.userName.compareTo(o2.userName);
}
}
This should work, I would suggest implementing Comparable interface for handling default sorting case...

if they have equal winratio to order them in terms of their username
This indicates that you should use userName for implementing comparable (for natural ordering). For winRatio, use comparator and in case of equal winRatio - also check their userName.

Related

Best way to combine multiple sorts in API 21

My goal is to apply 2 sorts on a list of objects made of string and boolean.
I have accounts and active/inactive states, so I want to show active first (sort boolean values) and then to sort rest of elements in alphabetical order.
For instance:
[John,inactive], [Craig,active], [Mike,inactive], [Dennis,innactive]
I want to have:
[Craig,active], [Dennis,innactive], [John,inactive], [Mike,inactive]
What I plan to do is to use Comparable<> but I wonder if there is another way to do that.
I don't want to use Guava or any additional libraries.
This should be used for Android API 21 as well, so list.sort() cannot be used.
Thanks in advance!
Simply create a new Comparator like this:
public class AccountComparator implements Comparator<Account> {
#Override
public int compare(Account o1, Account o2) {
if (o1.isActive() && !o2.isActive()) {
return -1;
}
if (!o1.isActive() && o2.isActive()) {
return 1;
}
return o1.getName().compareTo(o2.getName());
}
}
Minimal tested example:
public static void main(String[] args) {
Account account2 = new Account("B", true);
Account account4 = new Account("D", false);
Account account3 = new Account("C", true);
Account account1 = new Account("A", false);
List<Account> list = new ArrayList<>();
list.add(account1);
list.add(account2);
list.add(account3);
list.add(account4);
Collections.sort(list, new AccountComparator());
list.forEach(System.out::println);
}
with the expected output of
Account{name='B', active=true}
Account{name='C', active=true}
Account{name='A', active=false}
Account{name='D', active=false}
Or with a lambda expression: (Thanks to #Wow for using Comparator.comparing)
Collections.sort(list, Comparator.comparing(Account::isActive).reversed()
.thenComparing(Account::getName));
There's no magic/easy way to do it without Java 8 or some third-party library. You'll have to implement Comparable and do the heavy lifting yourself:
public class Person implements Comparable<Person> {
private final boolean isActive;
private final String name;
#Override
public int compareTo(Person other) {
if (isActive && !other.isActive) {
return -1;
} else if (!isActive && other.isActive) {
return 1;
} else {
return name.compareTo(other.name);
}
}
}

Need a sorted collection of pairs capable of performing floor operation based only on the first member of the pair

I appologize for the title. I don't know what this computational problem is called.
I was hoping there was a collection class ready for me to use to solve this. I use TreeSet in the example bellow just to show what I need. I believe the example to be clear enough to explain the question.
public class MyClass
{
//I know TreeSet doesn't work. Is there a collection that would work like TreeSet
//but has a method similar to the imaginary overload of TreeSet.floor I invoke in my code?
private TreeSet<OrderElement<Integer, Object>> treeSet = new TreeSet<>();
//this method is irrelevant to the problem.
//it's here just for usability of this example class
public void addElement(int order, Object element)
{
treeSet.add(new OrderElement(order, element));
}
//this is the method I need to implement
public Object floor(int order)
{
//this overload does not exist. I need a collection with a similar function
return treeSet.floor(order);
}
}
//this class represents the elements in the TreeSet.
public class OrderElement<O extends Comparable, E>, implements Comparable<O>
{
public final O order;
public final E element;
public OrderElement(O order, E element)
{
this.order = order;
this.element = element;
}
#Override
public int compareTo(O param)
{
return this.order.compareTo(param);
}
#Override
public boolean equals(Object obj)
{
return obj.equals(this.order);
}
#Override
public int hashCode()
{
return this.order.hashCode();
}
}
You want to use TreeMap<Integer, Object> and your floor method is called floorKey there
http://docs.oracle.com/javase/7/docs/api/java/util/TreeMap.html#floorKey(K)

Want to sort an ArrayList of objects by several different attributes

I've got this class called Block (each Block has an x value and a y value). I've created a bunch of these and put them all in a list. I've managed to sort the list by x-value:
public class Block implements Comparable<Block> {
...
public int compareTo(Block block2){
return this.x - block2.x;
}
But I want to be able to sort the list both by x and by y-value at my leisure. Is this doable somehow?
You can implement two Comparators:
enum BlockSort implements Comparator<Block> {
XSORT {
#Override
public int compare(Block b1, Block b2) {
return b1.x - b2.x;
}
},
YSORT {
#Override
public int compare(Block b1, Block b2) {
return b1.y - b2.y;
}
}
}
Then pass the appropriate instance XSORT/YSORT as a second argument to the sort method. For example:
Collections.sort(blockList, BlockSort.XSORT);
Additionally, if by-x isn't the natural ordering of Block instances, it would likely be wiser to not make Blocks Comparable at all.
You can use 2 Comparators to achieve this.
public class XComparator implements COmparator<Block>{
public int compare(T o1, T o2){
...
}
}
and :
public class YComparator implements COmparator<Block>{
public int compare(T o1, T o2){
...
}
}
Then use one of them to sort:
Collections.sort(list, new XComparator());
Collections.sort(list, new YComparator());
Much like #arshajii's answer, you could utilize an enum for the various different sorting methods, code shown below:
public enum BlockSorting implements Comparator<Block> {
X{
public int compare(final Block b1, final Block b2){
return b1.x - b2.x;
}
},
Y{
public int compare(final Block b1, final Block b2){
return b1.y - b2.y;
}
};
}
The advantage to using this way is that you could easily add more sorting methods (without creating a new enum each time). For example, to sort the collection of Blocks using the X, you could do something like this:
Collection<Block> blocks = ....;
Collections.sort(blocks, BlockSorting.X);
A solution with Enums;
public static enum Order implements Comparator {
ByX() {
public int compare(Block b1, Block b2) {
return b1.x - b2.x;
}
},
ByY() {
public int compare(Block b1, Block b2) {
// TODO: Should really use a collator.
return b1.y - b2.y;
}
};
Then you call it by,
Collections.sort(blocks, Order.ByX);

java compare to different options

I have implemented compareTo to allow me to compare my class' based on some criteria and it is working fine.
However, at some point I want to compare the class' on one thing and at another point in the code I want to compare the class based on another thing.
Is it possible to have two different implementations of compareTo and using one at some point and one at another?
In general the mechanism to do this is to implement one or more Comparators and use the appropriate one as needed.
Since your Class is "Comparable" you can use the compareTo, you can't - however - create more then one implementation of that function to be used at different points in the same Class (you have one function to override, and you can't do that twice).
You can, however, take a look at the Comparator Interface; and implementation of that interface can allow you to implement and use a different compareTo for your object.
We achieved something similar by writing a utility comparator for our class - something like this:
public class FooComparator implements Comparator<Foo> {
public static String COMPARE_FIELD1 = "COMPARE_FIELD1";
public static String COMPARE_FIELD2 = "COMPARE_FIELD2";
public static String COMPARE_FIELD3 = "COMPARE_FIELD3";
private String compareBy = COMPARE_FIELD1;
private boolean reverse = true;
public FooComparator(){}
public FooComparator(String sort){
compareBy = sort;
}
public void reverse() {
if(reverse) {reverse = false;
} else {reverse = true;}
}
public void field1Sort() {compareBy = COMPARE_FIELD1;}
public void field2Sort() {compareBy = COMPARE_FIELD2;}
public void field3Sort() {compareBy = COMPARE_FIELD3;}
public int compare(Foo foo1, Foo foo2) {
if(compareBy.equals(COMPARE_FIELD2)) {
return compareByField2(foo1, foo2);
} else if(compareBy.equals(COMPARE_FIELD3)) {
return compareByField3(foo1, foo2);
}
return compareByField1(foo1, foo2);
}
private int compareByField1(Foo foo1, Foo foo2) {
if(reverse) {return foo1.getField1().compareTo(foo2.getField1());}
return foo1.getField1().compareTo(foo2.getField1());
}
private int compareByField2(Foo foo1, Foo foo2) {
if(reverse) {return foo1.getField2().compareTo(foo2.getField2());}
return foo1.getField2().compareTo(foo2.getField2());
}
private int compareByField3(Foo foo1, Foo foo2) {
if(reverse) {return foo1.getField3().compareTo(foo2.getField3());}
return foo1.getField3().compareTo(foo2.getField3());
}
}
We then can use it like this:
List<Foo> foos = new ArrayList<Foo>();
FooComparator comparator = new FooComparator(FooComparator.COMPARE_FIELD1);
Collections.sort(foos, comparator);

Comparing an Array of Comparables in Java

I want to compare an array of comparables. The simplest way seems the following (details not shown):
public class ArrayComparable implements Comparable<ArrayComparable>{
ArrayList<Comparable<?>> list = new ArrayList<Comparable<?>>();
#Override
public int compareTo(ArrayComparable ac) {
Iterator<Comparable<?>> itr = ac.list.iterator();
for(Comparable<?> l : list) {
Comparable<?> itrNext = itr.next();
if(itrNext.getClass() == l.getClass()) {
if(itrNext.compareTo(l)) {
//something
} else {
//other stuff
}
} else {
//some other thing
}
}
}
Of course the problem here is that the compareTo as in itrNext.compareTo(l) will not work giving the error: The method compareTo(capture#6-of ?) in the type Comparable<capture#6-of ?> is not applicable for the arguments (Comparable<capture#7-of ?>)
which I understand why (as far as the method is concerned I might be comparing apples to oranges). On the other hand, I know I am not as I check for the class of things before comparing them.
So is there a way I can make this work? Don't worry about the sanity of comparing arrays of any comparables, as I have a good reason why I want to do that.
EDIT- SO why would I want to do something like this. Say I wanted to have an array of comparables, and I didn't care what was contained in each index, as long as the types corresponded, and they could be compared. Then I could do a general lexicographical compare between these arrays. This way I don't have to write a comparable for (int,int) and (int, string), and (string, double, string) or whatever you need. I just write one, and as long as I make sure that the types match (and I can), I am good to go.
Using the raw type Comparable wherever you're currently using Comparable<?> should work. Actually, you could just do that in one place if you want:
if (((Comparable) itrNext).compareTo(l) == 0)
Make ArrayComparable a generic class so that you can properly parameterize the generics rather than using <?> everywhere. Oh, and you might as well implement Iterable as well.
public class ArrayComparable<T> implements Comparable<ArrayComparable<T>>, Iterable<T>
{
List<Comparable<T>> list = new ArrayList<Comparable<T>>();
#Override
public int compareTo(ArrayComparable<T> ac)
{
// snip
}
#Override
public Iterator<T> iterator()
{
return list.iterator();
}
}
Try this:
if(itrNext.getClass().cast(itrNext).compareTo(l.getClass().cast(l))) {
//something
} else {
//other stuff
}
public class GenericDemo<T>{
T g;
public <T extends Comparable<T>> void printData(T a[]){
T max = a[0];
if(a[1].compareTo(max)>0){
max=a[1];
}
if(a[2].compareTo(max)>0){
max=a[1];
}
System.out.println(max);
System.out.println("DataType: " +a.getClass().getName());
}
public static void main(String[] ar)
{
Integer a[]={1,2,3};
Byte b[]= {4,6,7};
Short c[]={6,8,9};
GenericDemo g = new GenericDemo();
g.printData(a);
g.printData(b);
g.printData(c);
}
}
A good answer to this would be:
public final class ArrayComparable<T extends Comparable<T>>
implements Comparable<ArrayComparable<T>> {
private final ArrayList<T> list = new ArrayList<>();
#Override
public int compareTo(final ArrayComparable<T> other) {
final Iterator<T> it = other.list.iterator();
for (final T element : list) {
final T otherElement = it.next();
final int comparison = element.compareTo(otherElement);
if (comparison < 0) {
// something
} else if (comparison > 0) {
// other stuff
} else {
// other stuff
}
}
return 0;
}
}

Categories

Resources