How to sort a continuously updated (dynamic) object in an array

£可爱£侵袭症+ 提交于 2020-04-30 05:10:48

问题


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:

  1. playerList
  2. winRatio, which is set in the NimPlayer class with its getter.

The aim :

  1. to sort the winRatio descendingly, which means from the highest score to the lowest one. The ratio is calculated by score/gamePlayed.
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!