I am developing a game for android and I save the scores in a text file of the form "100&playername1,93&playername1,1950&playername2" etc. i.e. it is totally unordered.
Now I am trying to make a high score interface and I am aware that to sort the scores I should use String.split(",") followed by String.split("&")[0] to get the scores and put these in an ArrayList and then call Collections.compare(list). However once I have done that I then have no way of finding the names associated with the score.
Could anyone help me with this please. I have tried sorting the whole string in between brackets (putting the phrase "100&playername1" into the array, but that can't sort according to orders of magnitude. By this I mean it would put 100 ahead of 1950.
Thanks for any help!
Make a class called UsernameScorePair. Once you have split the scores and the usernames, put them in pairs (one for each "score&username").
For example, this class definition could work:
public class UsernameScorePair {
private String name;
private int score;
public UsernameScorePair(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
}
Make a class called UsernameScorePairComparator that implements Comparator.
In the compare(Object o1, Object o2) method, cast the Objects to UsernameScorePairs and compare the scores. For example:
public class UsernameScorePairComparator {
public UsernameScorePairComparator() {
}
public int compare(Object o1, Object o2) {
UsernameScorePair usp1 = (UsernameScorePair)o1;
UsernameScorePair usp2 = (UsernameScorePair)o2;
return usp1.getScore() - usp2.getScore();
}
}
Then use Collections.sort like this:
Collections.sort(listofpairs, new UsernameScorePairComparator())
I don't remember if it sorts in ascending order or descending order. If it's ascending order, then just change return usp1.getScore() - usp2.getScore(); to return usp2.getScore() - usp1.getScore();
EDIT
A Comparator basically compares two objects. In its compareTo method, it returns a negative value if the first is less than the second, a positive one if the first is greater than the second, and zero if they are both equal.
You can implement a Comparator (as I just did) to suit your needs. Then, using that Comparator, you can use standard API methods such as Collections.sort().
I hope this works!
You can iterate through the elements and create a new Player for each String like so:
public class Player implements Comparable<Player>{
private int mScore;
private final String mName;
public Player(final int score, final String name) {
mScore = score;
mName = name;
}
public void setScore(final int score) {
mScore = score;
}
#Override
public int compareTo(Player other) {
return mScore - other.mScore;
}
}
Now you just need to call Collections.sort(List<Player>); And you can even make the Player class implement the Serializable interface:
import java.io.Serializable;
public class Player implements Comparable<Player>, Serializable {
private static final long serialVersionUID = 8257815475849584162L;
private int mScore;
private final String mName;
public Player(final int score, final String name) {
mScore = score;
mName = name;
}
public void setScore(final int score) {
mScore = score;
}
#Override
public int compareTo(Player other) {
return mScore - other.mScore;
}
readObject(){...}
writeObject(){...}
}
A way is to have a custom object (call it Score) that contains the score and the player. Then put your scores in a list of Score, with a custom comparator that compares on scores.
final String wholeString = "100&playername1,93&playername1,1950&playername2";
final List<Score> scoresForPlayers = new ArrayList<Score>();
final String[] scores = wholeString.split(",");
for (String score : scores) {
final String[] split = score.split("&");
scoresForPlayers.add(new Score(Integer.parseInt(split[0]), split[1]));
}
Collections.sort(scoresForPlayers, new Comparator<Score>() {
#Override
public int compare(Score o1, Score o2) {
return o1.score.compareTo(o2.score);
}
});
class Score {
private final Integer score;
private final String player;
public Score(Integer score, String player) {
this.score = score;
this.player = player;
}
}
Related
I've been building a classical Nim game with three java classes. So far, I build almost everything and the last thing I need to do is to rank the player in descending order by winning ratio, which is the score divided by the gamePlayed. What I've tried was to implement the comparables in NimPlayer class. Here is my code:
public class NimPlayer implements Comparable<NimPlayer>{ //initialize comparable
private String userName;
private String familyName;
private String givenName;
static int counter;
private int score;
private int gamePlayed;
private int winRatio = score / (gamePlayed+1); //avoid 0/0, mathmatically wrong
static NimPlayer[] playerList = new NimPlayer[10]; // set an array here
//define NimPlayer data type
public NimPlayer(String userName, String surName, String givenName) {
this.userName = userName;
this.familyName = surName;
this.givenName = givenName;
}
// create new data using NimPlayer data type
public static void createPlayer(String userName, String familyName, String givenName) {
if (counter<10) {
playerList[counter++] = new NimPlayer(userName, familyName, givenName);
} else {
System.out.println("Cannot add more players.");
}
}
public static int getCounter() {
return counter;
}
public static NimPlayer [] getPlayer() {
return playerList;
}
// the getter and the setter of userName, familyName, givenName, score, gamePlayed
#Override
public String toString() {
return winRatio+"% | "+gamePlayed+" games | "+givenName+" "+familyName;
}
#Override
public int compareTo(NimPlayer o) {
return this.winRatio - o.winRatio;
}
}
In the main method, which called Nimsys, I've tried:
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (true) {
System.out.print('$');
String commandin = in.next();
if (commandin.equals("rankings")) {
Arrays.sort(NimPlayer.getPlayer());//sorting with the object type
System.out.println(Arrays.toString(NimPlayer.getPlayer()));
}
}
However, when I have two players in play and their score and gamePlayedare not null, the execution still goes to NullPointerException. Any help is highly appreciated.
The error you are getting is because you are initializating the array with a size of 10. The sort operation will traverse all 10 positions, and some of them will be null. If you create 10 players, then it should work without any problem.
To have a dynamic size, use an ArrayList instead, updating the following:
static List<NimPlayer> playerList = new ArrayList<>();
public static void createPlayer(String userName, String familyName, String givenName) {
if (counter < 10) {
playerList.add(new NimPlayer(userName, familyName, givenName));
counter++;
} else {
System.out.println("Cannot add more players.");
}
}
For sorting the List:
Collections.sort(NimPlayer.playerList);
Besides this, I think you have some flaws in your code.
First, you are initializing the winRatio in the field declaration and, at least from what I can see in your code, is not getting updated never, so it will be always 0. A way to overcome this is to use the getters and setters to trigger the calculation each time you ask for the value.
public Float getWinRatio() {
return Float.valueOf(getScore()) / (getGamePlayed() + 1);
}
Second, you set the winRatio as an int. This will ignore all the decimals in the divisions, so the results won't be accurate.
Also, you seem to be combining the data of players with the logic of the game. You should split the NimPlayer class from the logic containing all the players in the game.
I have an ArrayList composed of Student objects. These objects contain first name, last name, lab grade, project grade, exam grade, and total grade. I am trying to write a function that sorts the Student objects in the ArrayList based on their total grade.
Here's my Student class:
public class Student {
// fields
private String firstname;
private String lastname;
private int labgrade;
private int projectgrade;
private int examgrade;
private int totalgrade;
// constructor
public Student(String firstname, String lastname, int labgrade,
int projectgrade, int examgrade) {
this.firstname = firstname;
this.lastname = lastname;
this.labgrade = labgrade;
this.examgrade = examgrade;
this.totalgrade = labgrade + projectgrade + examgrade;
}
// method
public String toString() {
String s = firstname + " " + lastname + " has a total grade of "
+ totalgrade;
return s;
}
public int compareTo(Student s) {
return (totalgrade = s.totalgrade);
}
And here's what I tried to do to sort:
private ArrayList<Student> arraylist = new ArrayList<Student>();
public void SortStudent() {
Collections.sort(arraylist);
}
But that doesn't work because it says it can only work on List not ArrayList. Any help to fix my SortStudent method?
ArrayList is a List, the problem is that Student does not implement Comparable, and you didn't define the compareTo method, which you need to do to use the sort method from Collections.
You could do something similar to this:
public class Student implements Comparable<Student> {
//... the rest of the class
public int compareTo(Student s) {
return Integer.compare(this.grade, s.grade);
}
}
Another option would be to use lambda expressions with the method Collections.sort(List<T>, Comparator<? super T> c), where you don't need to implement Comparable:
public void sortStudent() {
Collections.sort(arraylist, (s1, s2) -> Integer.compare(s1.grade, s2.grade));
}
Collections.sort only works for types that implement Comparable:
public static <T extends Comparable<? super T>> void sort(List<T> list) {...}
Implementing Comparable<...> with Student is a bad choice in this case, since the sorting criteria are likely to change. e.g. sometimes you want to sort by first name, and sometimes by last name.
List<...> has a sort method that takes a Comparator, you could use that:
private ArrayList<Student> arraylist = new ArrayList<Student>();
...
public void SortStudent() {
arraylist.sort((x, y) -> Integer.compare(x.totalgrade, y.totalgrade));
}
class Student implements Comparable{
int rollno;
String name;
int grade;
Student(int rollno,String name,int grade){
this.rollno=rollno;
this.name=name;
this.grade=grade;
}
public int compareTo(Object obj){
Student st=(Student)obj;
if(grade==st.grade)
return 0;
else if(grade>st.grade)
return 1;
else
return -1;
}
public class Sorting {
Vector<Talk> sorty(Vector <Talk> s) {
Collections.sort(s);
return s;
}
}
I need to sort Objects of the class Talk but I keep getting the error of
"Bound mismatch".
Talk Class is something like this:
public class Talk {
String name;
int duration;
public int compareTo(Talk t) {
int compare = t.duration;
return this.duration - compare;
}
}
Your Talk class needs to implement the Comparable interface if you want to sort lists containing it:
public class Talk implements Comparable<Talk> {
Note that it is not safe to compare int values by subtracting them from each other, unless you know that both values are only going to be positive (or not more than Integer.MAX_VALUE apart, to be precise). The reason is that an int will overflow and incorrectly turn negative if the values are more apart than that. Doesn't happen a lot, but it's a difficult bug to trace if it does happen, so better to get yourself used to an alternative.
A better way to compare, which always works, is to call Integer.compare(int, int):
public int compareTo(Talk t) {
return Integer.compare(duration, t.duration);
}
Talk does implement a compareTo method, but it doesn't implement the Comparable interface, which is where the compareTo should be overriden from.
Change your Talk to:
public class Talk implements Comparable{
String name;
int duration ;
#Override
public int compareTo(Object o)
{ Talk t = (Talk)o;
int compare = t.duration;
return this.duration - compare;
}
}
For me, I always prefer a fixed -1,0 or 1 return:
#Override
public int compareTo(Object o)
{ Talk t = (Talk)o;
int compare = t.duration;
return this.duration == compare ? 0 : this duration > compare ? 1 : -1;
}
but this is not mandatory.
I would, however, recommend to reconsider the access modifiers of your variables. Usually, private variables are recommended over package visibility.
I hope i can help you.
public class TestSorting {
/**
* #Desc:
*
* #param args
*
* #Author: luochao
* #CreateTime: 2016年2月3日
*/
public static void main(String[] args) {
Vector<Talk> vector = new Vector<Talk>();
vector.add(new Talk("centos",3));
vector.add(new Talk("linux",1));
vector.add(new Talk("java",2));
}
}
class Talk implements Comparable<Talk> {
private String name;
private int duration;
public Talk(String name,int duration) {
this.name = name;
this.duration = duration;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
#Override
public int compareTo(Talk o) {
int compare = o.duration;
return -(this.duration - compare);
}
}
Im trying to figure out how to sort an ArrayList using comparable, my code looks like this:
public class playerComparsion{
public static void main(String[] args){
ArrayList<Object> list = new ArrayList<Object>();
Player p1 = new Players(1,92,Zlatan);
Player p2 = new Players(2,92,Hazard);
Player p3 = new Players(1,82,Klose);
list.add(p1);
list.add(p2);
list.add(p3);
}
}
class Players implements Comparable{
int position;
String name;
int rating;
public Players(int i, int j, String string) {
this.position=i;
this.rating=j;
this.name=string;
}
public void getRating() {
System.out.println(this.rating);
}
public void getPos() {
System.out.println(this.position);
}
public void getName() {
System.out.println(this.name);
}
#Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
return 0;
}
}
I want to sort the Arraylist based on the attribute rating. I suppose I should use the compareTo function but I have no idea how, can someone help me?
Instead of making Player implement Comparable, you get more flexibility by implementing Comparator<Player> classes. For example:
class PlayerComparatorByRating implements Comparator<Player> {
#Override
public int compare(Player o1, Player o2) {
return o1.getRating() - o2.getRating();
}
}
class PlayerComparatorByName implements Comparator<Player> {
#Override
public int compare(Player o1, Player o2) {
return o1.getName().compareTo(o2.getName());
}
}
After all, Player has multiple fields, it's easy to imagine that sometimes you might want to order players differently. A great advantage of this approach is the single responsibility principle: a Player class does only one thing, encapsulates player data. Instead of adding one more responsibility (sorting), it's better to move that logic in another class.
You could use these comparators with Collections.sort, for example:
Collections.sort(list, new PlayerComparatorByRating());
System.out.println(list);
Collections.sort(list, new PlayerComparatorByName());
System.out.println(list);
Extra tips
Your class seems to be named Players. It's better to rename to Player.
The getName, getRating, getPos methods should not return void and print the result, but return the field values instead.
Use better names for the constructor arguments, for example:
Player(int position, int rating, String name) {
this.position = position;
this.rating = rating;
this.name = name;
}
Use the right type of list to store players:
List<Player> list = new ArrayList<Player>();
Please format your code properly. Any IDE can do that.
Suggested implementation
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Player {
private int position;
private int rating;
private final String name;
Player(int position, int rating, String name) {
this.position = position;
this.rating = rating;
this.name = name;
}
public int getRating() {
return rating;
}
public int getPos() {
return position;
}
public String getName() {
return name;
}
#Override
public String toString() {
return String.format("%s:%d:%d", name, position, rating);
}
}
class PlayerComparatorByRating implements Comparator<Player> {
#Override
public int compare(Player o1, Player o2) {
return o1.getRating() - o2.getRating();
}
}
class PlayerComparatorByName implements Comparator<Player> {
#Override
public int compare(Player o1, Player o2) {
return o1.getName().compareTo(o2.getName());
}
}
public class PlayerComparatorDemo {
public static void main(String[] args){
List<Player> list = new ArrayList<Player>();
Player p1 = new Player(1, 92, "Zlatan");
Player p2 = new Player(2, 92, "Hazard");
Player p3 = new Player(1, 82, "Klose");
list.add(p1);
list.add(p2);
list.add(p3);
Collections.sort(list, new PlayerComparatorByRating());
System.out.println(list);
Collections.sort(list, new PlayerComparatorByName());
System.out.println(list);
}
}
Don't use a raw type with Comparable. Instead, use Comparable<Players>. This way, you have direct access to the object you care about without having to cast from Object.
The sample compareTo would be this:
public int compareTo(Player other) {
return rating - other.getRating();
}
Then, you would actually have to...sort it, using Collections.sort().
Collections.sort(list);
The reason for Comparable<Players> is that Comparable itself is defined as taking a generic type T.
Try this.
public class Comparator_Test {
public static void main(String[] args) {
ArrayList<Players> list = new ArrayList<Players>();
Players p1 = new Players(1,92,"Zlatan");
Players p2 = new Players(2,92,"Hazard");
Players p3 = new Players(1,82,"Klose");
list.add(p1);
list.add(p2);
list.add(p3);
PlayerComparator comparator = new PlayerComparator();
System.out.println(list);
Collections.sort(list, comparator);
System.out.println(list);
}
}
class Players {
int position;
String name;
int rating;
public Players(int i, int j, String string) {
this.position=i;
this.rating=j;
this.name=string;
}
public void getRating() {
System.out.println(this.rating);
}
public void getPos() {
System.out.println(this.position);
}
public void getName() {
System.out.println(this.name);
}
public String toString() {
return rating + "";
}
}
class PlayerComparator implements Comparator<Players> {
#Override
public int compare(Players o1, Players o2) {
if(o1.rating > o2.rating) {
return 1;
}
if(o1.rating < o2.rating) {
return -1;
}
return 0;
}
}
Quick example is a collection of users' first and last name.
One method requires that I compare using the first name, another using the last name. Is it possible to have two different compareTo()?
Or am I just better off creating two different value classes?
You can use the Comparator in java
In this example I m comparing the Fruit objects on the basis of fruitName using Comparable CompareTo method and quantity here by using Comparator ,if you want this object to be compare using fruitDesc then create one more static innerclass as I did for fruitName
public class Fruit implements Comparable<Fruit>{
private String fruitName;
private String fruitDesc;
private int quantity;
public Fruit(String fruitName, String fruitDesc, int quantity) {
super();
this.fruitName = fruitName;
this.fruitDesc = fruitDesc;
this.quantity = quantity;
}
public String getFruitName() {
return fruitName;
}
public void setFruitName(String fruitName) {
this.fruitName = fruitName;
}
public String getFruitDesc() {
return fruitDesc;
}
public void setFruitDesc(String fruitDesc) {
this.fruitDesc = fruitDesc;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int compareTo(Fruit compareFruit) {
int compareQuantity = ((Fruit) compareFruit).getQuantity();
//ascending order
return this.quantity - compareQuantity;
//descending order
//return compareQuantity - this.quantity;
}
public static Comparator<Fruit> FruitNameComparator
= new Comparator<Fruit>() {
public int compare(Fruit fruit1, Fruit fruit2) {
String fruitName1 = fruit1.getFruitName().toUpperCase();
String fruitName2 = fruit2.getFruitName().toUpperCase();
//ascending order
return fruitName1.compareTo(fruitName2);
//descending order
//return fruitName2.compareTo(fruitName1);
}
};
}
Using compareTo means that you are using the Comparable interface, which defines only one "natural order" for your class.
To have any other ordering, it's best to create a separate class that implements Comparator for each ordering you need. You don't need to create a different value class.
No you cannot, but you can create a separate Comparator for each ordering.