问题
I am building a classical Nim game. So far, I've done the player part and the game part. Now, I am trying to sort(rank) the object in an array. I've built the following for sorting:
playerList
winRatio
, which is set in theNimPlayer
class with its getter.
The aim :
- to sort the
winRatio
descendingly, which means from the highest score to the lowest one. The ratio is calculated byscore/gamePlayed
. - If there's a tie, sort using
userName
alphabetically.
I've referred to this issue: How to sort an array of objects in Java?
I know what I should do is use the Comparable
or Comparator
for sorting but from the article, they sort using the attributes in the object (All the information I've found so far). What I am dealing with is the continuously updated data outside the constructor.
What I've tried was directly printed out the data without sorting.
Here is my related code (NimPlayer
):
public class NimPlayer {
private String userName;
private String familyName;
private String givenName;
static int counter;
private int score;
private int gamePlayed;
static NimPlayer[] playerList = new NimPlayer[2]; // 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.");
}
// all the getter and setter related to the constructor; the getter of the player list.
}
public void setScore(int score) {
this.score=score;
}
public int getScore() {
return score;
}
public void setGamePlayed (int gamePlayed) {
this.gamePlayed = gamePlayed;
}
public int getGamePlayed() {
return gamePlayed;
}
public int getWinRatio () {
return Math.round(Float.valueOf(getScore())/ (getGamePlayed()+1)*100) ;
}
}
This is my main class (Nimsys
)
public static void searchAndPrintRankingData() {
for (int i = 0; i < NimPlayer.getCounter(); i++) {
String familyName = NimPlayer.getPlayer()[i].getFamilyName();
String givenName = NimPlayer.getPlayer()[i].getGivenName();
int score = NimPlayer.getPlayer()[i].getScore();
int gamePlayed = NimPlayer.getPlayer()[i].getGamePlayed();
double winRatio = score/(gamePlayed+1);//wrong calculation for testing
System.out.println(winRatio+"% | "+gamePlayed+" games | "+givenName+" "+familyName);
}
}
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")) {
String commandOrder = in.nextLine().trim();
if (commandOrder.equals("asc")) {
//sort the data
searchAndPrintRankingData();
}
if (commandOrder.equals("") || commandOrder.equals("desc")) {
//sort the data
searchAndPrintRankingData();
}
}
}
Any help is highly appreciated.
回答1:
Use Interface Comparator
Do it as follows:
import java.util.Arrays;
import java.util.Comparator;
class NimPlayer {
private String userName;
private String familyName;
private String givenName;
private static int counter;
private static final int SIZE = 4;
private int score;
private int gamePlayed;
static NimPlayer[] playerList = new NimPlayer[SIZE];
public NimPlayer(String userName, String surName, String givenName) {
this.userName = userName;
this.familyName = surName;
this.givenName = givenName;
}
public static void createPlayer(String userName, String familyName, String givenName) {
if (counter < SIZE) {
playerList[counter++] = new NimPlayer(userName, familyName, givenName);
} else {
System.out.println("Cannot add more players.");
}
}
public static int getCounter() {
return counter;
}
public static NimPlayer[] getPlayerList() {
return playerList;
}
public String getUserName() {
return userName;
}
public String getFamilyName() {
return familyName;
}
public String getGivenName() {
return givenName;
}
public void setScore(int score) {
this.score = score;
}
public int getScore() {
return score;
}
public void setGamePlayed(int gamePlayed) {
this.gamePlayed = gamePlayed;
}
public int getGamePlayed() {
return gamePlayed;
}
public int getWinRatio() {
return Math.round((Float.valueOf(score) / gamePlayed) * 100);
}
@Override
public String toString() {
return "User Name: " + userName + ", Name: " + givenName + " " + familyName + ", Score: " + score
+ ", Games Played: " + gamePlayed + ", Win ratio: " + getWinRatio();
}
}
public class Main {
static void searchAndPrintRankingData() {
NimPlayer[] players = NimPlayer.getPlayerList();
Arrays.sort(players,
Comparator.comparing((NimPlayer::getWinRatio)).reversed().thenComparing(NimPlayer::getUserName));
Arrays.stream(players).forEach(System.out::println);
}
public static void main(String[] args) {
NimPlayer.createPlayer("Avi", "Avinash", "Arvind");
NimPlayer.createPlayer("Harry", "Potter", "Harry");
NimPlayer.createPlayer("Vishy", "Anand", "Vishwanathan");
NimPlayer.createPlayer("Bond", "Bond", "James");
NimPlayer[] players = NimPlayer.getPlayerList();
players[0].setGamePlayed(2);
players[0].setScore(40);
players[1].setGamePlayed(3);
players[1].setScore(75);
players[2].setGamePlayed(2);
players[2].setScore(120);
players[3].setGamePlayed(4);
players[3].setScore(100);
System.out.println("Unsorted: ");
Arrays.stream(NimPlayer.getPlayerList()).forEach(System.out::println);
System.out.println();
System.out.println("Sorted on win ratio (then name, in case of tie): ");
searchAndPrintRankingData();
}
}
Output:
Unsorted:
User Name: Avi, Name: Arvind Avinash, Score: 40, Games Played: 2, Win ratio: 2000
User Name: Harry, Name: Harry Potter, Score: 75, Games Played: 3, Win ratio: 2500
User Name: Vishy, Name: Vishwanathan Anand, Score: 120, Games Played: 2, Win ratio: 6000
User Name: Bond, Name: James Bond, Score: 100, Games Played: 4, Win ratio: 2500
Sorted on win ratio (then name, in case of tie):
User Name: Vishy, Name: Vishwanathan Anand, Score: 120, Games Played: 2, Win ratio: 6000
User Name: Bond, Name: James Bond, Score: 100, Games Played: 4, Win ratio: 2500
User Name: Harry, Name: Harry Potter, Score: 75, Games Played: 3, Win ratio: 2500
User Name: Avi, Name: Arvind Avinash, Score: 40, Games Played: 2, Win ratio: 2000
Note: In case you wish to use ArrayList<NimPlayer>
in searchAndPrintRankingData
, given below is the code for the same:
static void searchAndPrintRankingData() {
List<NimPlayer> players = new ArrayList<NimPlayer>(Arrays.asList(NimPlayer.getPlayerList()));
Collections.sort(players,
Comparator.comparing((NimPlayer::getWinRatio)).reversed().thenComparing(NimPlayer::getUserName));
players.stream().forEach(System.out::println);
}
回答2:
For sort ascending use the code link you mentioned. As long as the players have been added to the array, you can sort them. The code below is the key (you will need to adapt variable names etc) You will need to add winRation etc to NimPlayer constructor and as element of the array, then it can sort it.
@Test
public void sortBooks() {
Book[] books = {
new Book("foo", "1", "author1", "pub1"),
new Book("bar", "2", "author2", "pub2")
};
// 1. sort using Comparable
Arrays.sort(books);
System.out.println(Arrays.asList(books));
// 2. sort using comparator: sort by id
Arrays.sort(books, new Comparator<Book>() {
@Override
public int compare(Book o1, Book o2) {
return o1.id.compareTo(o2.id);
}
});
System.out.println(Arrays.asList(books));
}
Change this :
return o1.id.compareTo(o2.id);
To:
return o1.winRatio.compareTo(o2.winRatio);
回答3:
It should work by implementing the Comparable
interface, as the compareTo
method will be called each time you want to sort and will have the updated data.
To sort first by winRatio
, and if equal, by name
, it can work with the following code:
public class NimPlayer implements Comparable<NimPlayer> {
...
@Override
public int compareTo(NimPlayer o) {
// get the comparison of the win ratios
int ratioCompare = this.getWinRatio().compareTo(o.getWinRatio());
// if the winRatio is equal, return the reverse comparison of the usernames
return (ratioCompare != 0) ? ratioCompare : o.userName.compareTo(this.userName);
}
}
Then, to sort the array, you just need to use the Arrays
class:
Arrays.sort(NimPlayer.playerList);
Arrays.sort(NimPlayer.playerList, Collections.reverseOrder()); // for desc order
The problem you may face with that is if you always want to have the username ordered ASC. In that case, your only option is to implement two different comparators, one for ascending and another for descending orders:
Comparator<NimPlayer> ascComparator = (p1, p2) -> {
int ratioCompare = p1.getWinRatio().compareTo(p2.getWinRatio());
return (ratioCompare != 0) ? ratioCompare : p1.getUserName().compareTo(p2.getUserName());
};
Comparator<NimPlayer> descComparator = (p1, p2) -> {
int ratioCompare = p2.getWinRatio().compareTo(p1.getWinRatio());
return (ratioCompare != 0) ? ratioCompare : p1.getUserName().compareTo(p2.getUserName());
};
To sort using those comparators, just pass the comparators into the Arrays.sort
method:
Arrays.sort(NimPlayer.playerList, ascComparator);
Arrays.sort(NimPlayer.playerList, descComparator);
回答4:
Your NimPlayer
needs to implement Comparable
interface:
public class NimPlayer implements Comparable<NimPlayer> {
@Override
public int compareTo(NimPlayer other) {
if (other.getWinRatio() == this.getWinRatio()) {
return this.getUserName().compareTo(other.getUserName());
} else if (other.getWinRatio() > this.getWinRatio()) {
return 1;
}
return -1;
}
public static NimPlayer[] getPlayer() {
Arrays.sort(playerList);
return playerList;
}
}
Also, you need to fix the size of your playerList, which is set to 2 static NimPlayer[] playerList = new NimPlayer[2];
, and then you try to add up to 10 players :)
And when adding players to the list you'd better compared counter
to actual playerList.length;
来源:https://stackoverflow.com/questions/61402212/how-to-sort-a-continuously-updated-dynamic-object-in-an-array