Solved

Java: How to Sort

Posted on 2011-09-10
33
407 Views
Last Modified: 2012-05-12
I am creating a poker game and having a bit of difficulty with sorting. In particular I am working with a method (Poker Hand - Straight) which accepts an ArrayList.

I need to take this ArrayList and store it in some kind of a "SORTABLE" List.

All I have right now for the Straight Method is:

public void five_card_straight(ArrayList<Card> s)
   {
      // ArrayList s holds objects of type Card. Card has 2 private variables -- Rank & Suit.
      // The objects passed as S are: J 9 8 T 7 -- All of these are chars and T stands for 10.
   }

How do I sort it as 7 8 9 T J?

Professor gave me a hint -- "Comparable".
Completely forgot how to sort.
0
Comment
Question by:InfoTechEE
  • 20
  • 10
  • 3
33 Comments
 
LVL 47

Expert Comment

by:for_yan
ID: 36517052


Interface Collections hads metrhod Collections.sort(list, Comparator)
you just need to define Comparator in such a way that it knows what card order you want to impose
0
 
LVL 47

Expert Comment

by:for_yan
ID: 36517079


This is an example - not quite your case - but it shows you that
you can define compare methjod inside comparator anyway you want (I just compared the lengths if the string here) and you
also ca use Card objects instead of String - and then you'll define ytour own ordering.
I'll come up with the example closer to your case thatn this one
but jhope this woul give you a clue also:

you just need to define comare to method in your Card object correct,ly:

   ArrayList<String> ar = new ArrayList<String>();
        ar.add("7");
        ar.add("8");
        ar.add("King");
        ar.add("3");

        Collections.sort(ar, new Comparator<String>() {
            public int compare(String o1, String o2) {
                
                
                if(o2.length() < o1.length())return 1;
                
                else return -1;
               // return 0;  //To change body of implemented methods use File | Settings | File Templates.
            }
        });

Open in new window


output:

[7, 8, 3, King]

Open in new window

0
 
LVL 47

Expert Comment

by:for_yan
ID: 36517107

This is how it works with calss Card:

calss Card:

class Card {
    static String [] ranks = {"2","3","4","5","6","7","8","9","10","J","D","K","A"};

     String rank;
    int value;

         Card(String s){
             rank = s;
                 for(int j=0; j< ranks.length; j++) {
             if(ranks[j].equals(getRank())){
                 value = j;
                 break;
             }

         }
         }

    String getRank(){return rank;}
     int compareTo(Card c){
         int forRank =0;
         for(int j=0; j< ranks.length; j++) {
             if(ranks[j].equals(c.getRank())){
                 forRank = j;
                 break;
             }

         }
          if(forRank<value) return 1;
         else  return -1;

     }

    public String toString(){
        return rank;
    }

}

Open in new window


sorting code:
    ArrayList<Card> ar = new ArrayList<Card>();
        ar.add(new Card("7"));
        ar.add(new Card("8"));
        ar.add(new Card("K"));
        ar.add(new Card("3"));

        Collections.sort(ar, new Comparator<Card>() {
            public int compare(Card o1, Card o2) {

            return o1.compareTo(o2);

               // return 0;  //To change body of implemented methods use File | Settings | File Templates.
            }
        });

          System.out.println(ar);

Open in new window


Ouptput:
[3, 7, 8, K]

Open in new window

0
 
LVL 47

Expert Comment

by:for_yan
ID: 36517115
So I gave you my example - but you need to do it based on yout implmenntation of class Card
I hope though that you would understand from my example that you need to
implement method compareTo in your class Card which should ensure that it
retutrns negative number if the argument Card  is bigger than the Card on which you invoke the method
and positive number in the opposite situation.
With this method you'll be able to use it in Comparator which you can use to sort the ArrayList

Let me know if you have any questions.

0
 
LVL 47

Expert Comment

by:for_yan
ID: 36517117
In my Card class the ranks are Strings, but you can do of cousre
quite similar thing is the ranks will be char - just keep in mind that
for equality of char you shoud use == operator rather than the equals()
method which I used for Strings
0
 
LVL 47

Expert Comment

by:for_yan
ID: 36517140
another option which probably is closer to what your profesro meant is
that you should put your cards from ArrayList say to the TreeSet which maintains the
order of the elements - either natural order id the emlemnets say are Strings
ort the order imposed bytthe compareTo method

In order to implemnet this way you should declare at the top of yoyu Card class
that it implmennts Comparable and then in the body of the class yuou shoyud define compareTo()
method - and after you do it correctly - then just adding all elements to TreeSet
will make them ordered the way you defined in the compraeTo() method
Try to do it yourself - we can then look at your code if there are any problems
0
 
LVL 47

Expert Comment

by:for_yan
ID: 36517151
In any case you can still use similar compareTo method based on the
index of the Card in the array of ranks, as I used in my code
(even though the ranks can be either Strings or char's)
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 36517495
You don't actually need a loop. You can work out the rank ordering quite simply with
final static String RANK = "2345678910JDKA";
int rank = RANK.indexOf(candidateCard.getRank()); // You can do the same for suit

Open in new window

0
 

Author Comment

by:InfoTechEE
ID: 36530776
Thanks, but I'm not fully understanding all of it. I am a beginner in Java. What are we trying to do here? I will add my Card class code, and my Hand class code below.
public class Card {
	private char rank;
	private char suit;
	
	public Card()
	{
		
	}
	
	public Card(char first, char second)
	{
		rank = first;
		suit = second;
	}
	
	public char getRank()
	{
		return rank;
		
	}
	
	public char getSuit()
	{
		return suit;
	}
	
}

Open in new window

import java.util.*;

public class Hand {
   private boolean isFive_card_straight = false;

public void five_card_straight(ArrayList<Card> s)
	{
		//char [] straight = new char[s.size()];
		Card newCard = new Card();
		char objRank[] = new char[s.size()];
		
		for(int i=0;i<s.size();i++)
		{
			newCard = s.get(i);
			objRank[i]=newCard.getRank();
		}	
		Collections.sort(objRank); // What do I need
// to do here? Why can't Collections.sort simply sort objRank?		
()); // You can do the same for suit
	}

Open in new window

0
 
LVL 47

Expert Comment

by:for_yan
ID: 36531098
Your arraylist is made up of objects Card
In order to use Collctions.sort(..) wwith ArrayList of cards as the argument
the program needs to know when it compares two elements of type Card - which one
is "bigger" than another
If you array list would be made up of Strings then the comparison betwen them is already defined in class String and
Collections.sort(...) wil have no problems.
But Card is your own cuisstom object so you need to define whene you compare such two objects of class Card
how to determine which one is bigger .

One way would be to define compareTo ethod inside the class Card, but that sioolution will not be
very good for uyiu as in sopme cases you want to compare cards by rank , in another cases you'll want
to compare them by suite - so one method which will
compare only by rank wuithin the class card will not be veruy conveninent for you

For suvh cases CVollections.sort(..)
mwethod has anither form with two parameters - first one is
the collection which you want to sort; second one is the
instance of a class which defines one method - how to compare
two elements of the type which make up your collection.

This instance including this method can be defined in the fly withing the call to the sort method
so that dependeing on how you define it in the call to Collections.sort you'll be able
in one case sort them by rank in another case to siort them by suite
 
0
 
LVL 47

Expert Comment

by:for_yan
ID: 36531130


>Collections.sort(objRank); // What do I need
>// to do here? Why can't Collections.sort simply sort objRank?      

No, yoour goal is tnot to sort ranks - you want to sort your Card objects
in the arraylist - your Card is more than just rank - it laso has a suit,
so it is an instance of an object which contains both and it is a different entity than just
rank
 
0
 

Author Comment

by:InfoTechEE
ID: 36531155
OK, i'm starting to understand this more clearly.


One way would be to define compareTo ethod inside the class Card, but that sioolution will not be
very good for uyiu as in sopme cases you want to compare cards by rank , in another cases you'll want
to compare them by suite - so one method which will
compare only by rank wuithin the class card will not be veruy conveninent for you

In regard to above statement, you say it might now be too good for me since later on I might need to compare suit as well. Does that mean I should not touch the Card class? If I use Collections.sort(..) with 2 parameters, do I still have to add anything to the Card class?
0
 
LVL 47

Accepted Solution

by:
for_yan earned 500 total points
ID: 36531268
Look at my code in ID:36517107, at the second box
When I was writing it i was not thinking about comapring by suit

So I inclusded method compareTo(Card) into class Card

but to be more flxibel now what we can do we can rename that method to
compareToByRank(Card)

and write very similar method

compareToBySuit(Card)

and then inside the code in here:

    Collections.sort(ar, new Comparator<Card>() {
            public int compare(Card o1, Card o2) {

            return o1.compareTo(o2);

               // return 0;  //To change body of implemented methods use File | Settings | File Templates.
            }
        });

Open in new window

we ca modify it this way:

    Collections.sort(ar, new Comparator<Card>() {
            public int compare(Card o1, Card o2) {

            return o1.compareToByRank(o2);

               // return 0;  //To change body of implemented methods use File | Settings | File Templates.
            }
        });

Open in new window


and when you need to sort them by suit, you'll be able to use very sismilar code:

    Collections.sort(ar, new Comparator<Card>() {
            public int compare(Card o1, Card o2) {

            return o1.compareToBySuit(o2);

               // return 0;  //To change body of implemented methods use File | Settings | File Templates.
            }
        });

Open in new window


Let me knwoo if it is clear.

 
0
 
LVL 47

Expert Comment

by:for_yan
ID: 36531311
Of course with suits you'll have to modify your class Card so that it has suit varaible also
and takle two variables in the constructor, etc.
0
 

Author Comment

by:InfoTechEE
ID: 36531363
Great, that answers that question.

Another question is, what does the Card class IMPORT?
What does the Hand class IMPORT?

For the Card class do I have to write implements Comparator<Card>?
Do I have to write something similar for Hand class?
0
 
LVL 47

Expert Comment

by:for_yan
ID: 36531447
If you define your sorting as a parameter of Collections.sort() as we discussed
you don;'t need to declare your card implementing any interface as with that
you'll be able to provide only one method (and it cannot veven take parameter),
so it will be less flexible system

About Hand - i don't know - if tghere is a point in your application
where you want to sort some collection of Hand's and if the algorithm which is bifgger for hands
will always wbe the same then it willmake sense
to sya implement Comparable and implwemetmet method int compareTo
then tyou'll nbe able to sort collection of Hands with just one-parameter Collections.sort() method
and it will use that method which you defined tio implement the interface
It depends wif sucvh need arises - for the taks which you have now you don't need
to compare hands.
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 47

Expert Comment

by:for_yan
ID: 36531456
what you ean by this question:
>what does the Card class IMPORT?

If your caklses belong to different packages you may want to have import statement
at the top. if theye are in one package all - and so inone folder - you don't need to import them in the code
0
 

Author Comment

by:InfoTechEE
ID: 36531806
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
     ArrayList<Card> ar = new ArrayList<Card>();
        ar.add(new Card("7"));
        ar.add(new Card("8"));
        ar.add(new Card("K"));
        ar.add(new Card("3"));

        Collections.sort(ar, new Comparator<Card>() {
            public int compare(Card o1, Card o2) {

            return o1.compareTo(o2);

               // return 0;  //To change body of implemented methods use File | Settings | File Templates.
            }
        });

          System.out.println(ar);

Open in new window


I would like to go over the sorting code. You build an ArrayList of Cards, and populate it.

Then you are trying to sort it.

1. Inside the 2nd sort parameter -- Comparator<Card>(), you are creating a method called compare which accepts 2 objects, yet you never call or use that method.

I understand that compareToRank is a method inside my Card class, but before you can even call that you need to be passed Card o1 and Card o2. Where in your code does that happen? I'm sorry but I'm not fully understanding the sorting code.

The rest makes perfect sense.
0
 
LVL 47

Expert Comment

by:for_yan
ID: 36531841
you don't need explicitly to call compare method yourself

it is the implementation of the Collections.sort(... , ...) method
which will call it - it will take second parameter find the compare method associated
with the provided instnce of comparator and call this compare method internally
- it will actually call it inside many times according to the algorithm of sorting
Anyway, you don't need to call it explicitly yourself - you just provide it to the sort method
0
 

Author Comment

by:InfoTechEE
ID: 36531884
I guess my question was that I saw the compare method accepting two objects.

And it makes perfect sense that when you compare something you need to compare 2 things.

When I'm working with your sorting code, oooooooooooooooooooohhhhhhhhhh.

I think it hit me.

I was going to say when I'm working with the sorting code, I need to pass two objects to compare. But I am not realizing that I am sending more than 2 objects. I'm sending an entire list. And then the Comparator takes that list and automatically assigns and deals with which object is 1 and which is 2?

Am I on the right track now?
0
 
LVL 47

Expert Comment

by:for_yan
ID: 36531944
In general when you define method which  compares two object you can
devise it in any way - taking two parameteres and being say static mnethod or taking one paraemeter
and being the instance metyhod of the intance - if it is that way it assumes that you comparre
the argument with the instance on which you invoke the method.

However the format of method which is defined by the comapartor objkect which sort takes as the second parameter
is strictly fdfeined by the Comparator interface so you need to follow this defininition
when you write the contents of this secomnd parameter - so interfcae Comparator defines it this way:

http://download.oracle.com/javase/6/docs/api/java/util/Comparator.html#compare%28T,%20T%29

so we nedd to follow it strictly, becuae second parameter of the sort method  should implemnt interface Comparator

and you are write that to the sort method you are sensing the entire list as parameter 1 and the method how to comparee elements of the list
as paraeneter 2 - so this comparator intercae is the device to provide the algorithm as parameter of the method




0
 

Author Comment

by:InfoTechEE
ID: 36532025
A question about your Card class code. At the end you have:

public String toString(){
        return rank;
    }

What does that do?
0
 

Author Comment

by:InfoTechEE
ID: 36532059
When I try to debug my Straight Method I get an error. It says source not found.

Arrays.mergeSort(Object[], Object[], int, int, int, Comparator) line: not available      

I'll attach my current code for the method, and the entire Card class.

Card Class
 
public class Card {
	private char rank;
	private char suit;
	private int value;
	static char [] ranks = {'A','2','3','4','5','6','7','8','9','T','J','Q','K'};
	
	public Card() {}
	public Card(char first, char second)
	{
		rank = first;
		suit = second;
		
        for(int j=0;j<ranks.length;j++) 
        {
        	if(ranks[j]==getRank())
        	{
        		value=j;
        		break;
        	}
        }
	}	
	public char getRank()
	{
		return rank;
		
	}
	public char getSuit()
	{
		return suit;
	}
	
	int compareToRank(Card c){
        int forRank=0;
        for(int j=0;j<ranks.length;j++) {
            if(ranks[j] == c.getRank()){
                forRank = j;
                break;
            }
        }
         if(forRank<value) return 1;
        else  return -1;
    }
}

Open in new window

Method
 
public void five_card_straight(ArrayList<Card> s)
	{
		ArrayList<Card> p = new ArrayList<Card>();
		p.addAll(s);
        Collections.sort(p, new Comparator<Card>() {
            public int compare(Card o1, Card o2) {

            return o1.compareToRank(o2);
            }
        });
          
	}

Open in new window

0
 
LVL 47

Expert Comment

by:for_yan
ID: 36532085

More or less in the same way as wwith sort(...) method using the method to compare elements
there are some other methods which internally use methods of Object class (objects of all classes
inherit form Object, so any objects should have these method) to do something with the instances
of these classes.

To understand it here is an example: if I say

MyClass mycl = new MyClass();
System.out.println(mycl);

then println() method will lok at object mycl, discover that it is an instance of
MyClass() then it will find method
public String toString() {
}
within this class and then invoke this method on mycl get back the String
and print this string

So I defined this method, so that if
somewere I want to print an instance of Card
it would print rank for me

So Card c = new Card("K");
System.out.println(c);

will print just "K"

Of cousesre that class was not very good the card shoul also hacve a suit.
Then you should redefine the toString() mwethod
so that when you print instance of Card you'd get ionfoermation both about the rank and about the suit

like

Card c = new Card("K","H");
Systerm,out.println(c);
should print for example
"K of H";

for that you need to define
toString() method like that:

public toString(){
String s = ramnk + " of " + suit;
return s;


}
 
0
 
LVL 47

Expert Comment

by:for_yan
ID: 36532112

wheer is that:

Arrays.mergeSort(Object[], Object[], int, int, int, Comparator)  ?


Paste the whole code which you are trying to compuile
In waht class is your five_card_stright method?

0
 
LVL 47

Expert Comment

by:for_yan
ID: 36532246
this is te application which cantains two classes will look like
If you execute it (satrrting form the class Straight
which containd wthe main method) you'llget the outpurt below
(I added only three cards - not fivbe - to reduce typing; it will of course
worjk the asame for 5 cards; I modified both cklasses):


public class Card {
	private char rank;
	private char suit;
	private int valueRank;
	static char [] ranks = {'A','2','3','4','5','6','7','8','9','T','J','Q','K'};

	public Card() {}
	public Card(char first, char second)
	{
		rank = first;
		suit = second;

        for(int j=0;j<ranks.length;j++)
        {
        	if(ranks[j]==getRank())
        	{
        		valueRank=j;
        		break;
        	}
        }
	}
	public char getRank()
	{
		return rank;

	}
	public char getSuit()
	{
		return suit;
	}

	int compareToRank(Card c){
        int forRank=0;
        for(int j=0;j<ranks.length;j++) {
            if(ranks[j] == c.getRank()){
                forRank = j;
                break;
            }
        }
         if(forRank<valueRank) return 1;
        else  return -1;
    }

    public String toString(){
        return (rank + " of " + suit);
    }
}

Open in new window


import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Straight {
public static ArrayList five_card_straight(ArrayList<Card> s)
	{
		//ArrayList<Card> p = new ArrayList<Card>();
	//	p.addAll(s);
        Collections.sort(s, new Comparator<Card>() {
            public int compare(Card o1, Card o2) {

            return o1.compareToRank(o2);
            }
        });

        return s;
	}

    public static void main(String[] args) {
        ArrayList<Card> ar = new ArrayList<Card>();

        ar.add(new Card ('K','C'));
         ar.add(new Card ('7','S'));
         ar.add(new Card ('2','H'));

          ArrayList ar1 = five_card_straight(ar);

        System.out.println(ar1);

        
   }

}

Open in new window


Output below:

[2 of H, 7 of S, K of C]

Open in new window

0
 

Author Comment

by:InfoTechEE
ID: 36533243
   public String toString(){
        return (rank + " of " + suit);
    }

Works great when rank and suit are strings. What if they are CHARS?
0
 
LVL 47

Expert Comment

by:for_yan
ID: 36533286
In my example as you see rank and suit are chars,
sill when you write

rank + " of " + suit

 then

char + String + char

JVM automatically converts this to String

Therfore it works in my example, where as you see rank and suit are chars

It should work with chars also - nothing tio worry about it - worked for me - sghould work for you either
 
0
 

Author Comment

by:InfoTechEE
ID: 36533344
Wow...JAVA is a confusing beast.

public String toString(){
        return rank;
}

The above code doesn't work, complains about return type expected String but is Char.

public String toString(){
        return (rank + " of " + suit);
    }

However, the above code all of a sudden it works.
0
 
LVL 47

Expert Comment

by:for_yan
ID: 36533357
yes that is exactly becuae when you perform "+" between String and char the result is
concatentated String

So if you wanted to make that method work only with rank, you could do:



public String toString(){
        return ("" + rank);
}

You conatenat with empty String and it works

and this

public String toString(){
        return rank;
}

causes compiler errort because you promised to return String, and instead returning char






0
 
LVL 86

Expert Comment

by:CEHJ
ID: 36535355
You don't need

a. loops
b. separate methods for suit and rank comparison

If you're interested in doing it properly, let me know
0
 

Author Comment

by:InfoTechEE
ID: 36537257
What's the other option? Logical comparison?
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 36537629
Given the fact that you're using char as your type, then the comparison would stay the same. Only the implementation would change. I'm not entirely sure of the comparison rules though. Is

a. a 3 of hearts > 2 of clubs?

b. a 3 of hearts > 3 of clubs?
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
scoreUp challenge 14 48
Fibonacci challenge 11 84
Eclipse Neon and jdk 1.8.0 11 72
Systems talking to each other 5 110
After being asked a question last year, I went into one of my moods where I did some research and code just for the fun and learning of it all.  Subsequently, from this journey, I put together this article on "Range Searching Using Visual Basic.NET …
This was posted to the Netbeans forum a Feb, 2010 and I also sent it to Verisign. Who didn't help much in my struggles to get my application signed. ------------------------- Start The idea here is to target your cell phones with the correct…
Viewers learn how to read error messages and identify possible mistakes that could cause hours of frustration. Coding is as much about debugging your code as it is about writing it. Define Error Message: Line Numbers: Type of Error: Break Down…
Viewers will learn one way to get user input in Java. Introduce the Scanner object: Declare the variable that stores the user input: An example prompting the user for input: Methods you need to invoke in order to properly get  user input:

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now