Hashmap question.

Discussion in 'Spigot Plugin Development' started by avighnash, Jun 2, 2016.

  1. I am developing a plugin for my network, and I need some help with a hashmap. Here is the class:

    Code (Text):
    package us.universalpvp.l.managers;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.UUID;

    import org.bukkit.entity.Player;
    import org.bukkit.metadata.FixedMetadataValue;

    import us.universalpvp.l.Leagues;

    public class League {

        private UUID leader;
        private List<Player> officers;
        private List<Player> members;
        private String name;

        public League(UUID leader, String name) {
            this.leader = leader;
            this.name = name;
            this.members = new ArrayList<>();

        }

        public UUID getLeader() {
            return leader;
        }

        public void setLeader(UUID leader) {
            this.leader = leader;
        }

        public List<Player> getMembers() {
            return members;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public List<Player> getOfficers() {
            return officers;
        }

        public void addMember(Player player) {
            if (getMembers().contains(player)) {
                return;
            } else {
                this.members.add(player);
                player.setMetadata(getName(), new FixedMetadataValue(Leagues.getInstance(), 0));
            }
        }

        public void removeMember(Player player) {
            if (getMembers().contains(player)) {
                members.remove(player);
                player.removeMetadata(getName(), new Leagues());
            } else {
                return;
            }
        }

        public Player getMember(Player member) {
            if (getMembers().contains(member)) {
                return member;
            } else {
                return null;
            }
        }

        public boolean hasMember(Player member) {
            if (getMembers().contains(member)) {
                return true;
            } else {
                return false;
            }
        }

    }
    and here is the manager for the class:

    Code (Text):
    package us.universalpvp.l.managers;

    import java.util.List;
    import java.util.Map;
    import java.util.UUID;

    import org.bukkit.entity.Player;

    public class ManageLeagues {

        private Map<League, String> leagues;

        public Map<League, String> getLeagues() {
            return leagues;
        }

        public List<String> getLeagueNames() {
            List<String> names = (List<String>) leagues.values();
            return names;
        }

        public League getLeague(String leagueName) {
            for (Map.Entry<League, String> e : leagues.entrySet()) {
                League league = e.getKey();
                if (e.getValue().equals(leagueName)) {
                    return league;
                }

            }
            return null;
        }

        public void createLeague(UUID leader, String name) {
            if (getLeagues().containsValue(name)) {
                return;
            } else {
                League newLeague = new League(leader, name);
                leagues.put(newLeague, name);
            }
        }

        public void removeLeague(String name) {
            if (!leagues.containsValue(name)) {
                return;
            } else {
                leagues.values().remove(name);
            }
        }

        public boolean isInLeague(Player member, String name) {
            for (Map.Entry<League, String> e : leagues.entrySet()) {
                League league = e.getKey();
                if (e.getValue().equals(name)) {
                    if (league.hasMember(member)) {
                        return true;
                    }
                }

            }
            return false;
        }

    }
    the only problem, is that I want to check if a player is in a league, any of the leagues that are in the hashmap. The isInLeague can be used only if you know the name of the league. I know there are other solutions to do this, but I want to know the best way.

    Thanks.
     
  2. if you want to check player's league through hashmap use the code
    Code (PHP):
    if(leagues.containsKey(League league// league where player belongs)) {
    // do something
     
  3. Did you not read my post? I do not know what the name of the league the player is in or belongs to.
    UPDATED LEAGUE CLASS:

    Code (Text):
    package us.universalpvp.l.managers;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.UUID;

    import org.bukkit.entity.Player;

    import us.universalpvp.l.Leagues;

    public class League {

        private UUID leader;
        private List<Player> officers;
        private List<Player> members;
        private String name;

        public League(UUID leader, String name) {
            this.leader = leader;
            this.name = name;
            this.members = new ArrayList<>();

        }

        public UUID getLeader() {
            return leader;
        }

        public void setLeader(UUID leader) {
            this.leader = leader;
        }

        public List<Player> getMembers() {
            return members;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public List<Player> getOfficers() {
            return officers;
        }

        public void addOfficer(Player officer) {
            if (getOfficers().contains(officer)) {
                return;
            } else {
                this.officers.add(officer);
            }
        }

        public void demoteOfficer(Player officer) {
            if (getOfficers().contains(officer)) {
                this.officers.remove(officer);
                this.members.add(officer);
            } else {
                return;
            }
        }

        public void kickOfficer(Player officer) {
            if (this.isOfficer(officer)) {
                demoteOfficer(officer);
                removeMember(officer);
            }
        }

        public void addMember(Player player) {
            if (getMembers().contains(player)) {
                return;
            } else {
                this.members.add(player);
            }
        }

        public void removeMember(Player player) {
            if (getMembers().contains(player)) {
                members.remove(player);
                player.removeMetadata(getName(), new Leagues());
            } else {
                return;
            }
        }

        public Player getMember(Player member) {
            if (getMembers().contains(member)) {
                return member;
            } else {
                return null;
            }
        }

        public boolean hasMember(Player member) {
            if (getMembers().contains(member)) {
                return true;
            } else {
                return false;
            }
        }

        public boolean isOfficer(Player officer) {
            if (!getOfficers().contains(officer)) {
                return false;
            } else {
                return true;
            }
        }

    }
     
  4. what about compare if the player isn't in the list members if they aren't belong which means they are not in any league.
     
  5. I made this:

    Code (Text):
    public boolean isInLeague(Player member) {
            for (League league : leagues.keySet()) {
                if (league.hasMember(member)) {
                    return true;
                } else {
                    return false;
                }
            }
            return false;
        }
    Are there any better ways?
     
  6. else statement is not necessary to that case.
     
    • Like Like x 1
    • Agree Agree x 1
  7. So, any better ways?5
     
  8. A neater way would be to have 2 maps. A map of <String, League> where the string is the league name, and a map of <UUID, String> where UUID is player UUID and String is the name of their league. That way you can quickly find the league of a player even if there are quite a lot of leagues. However the disadvantage to this is that you may end up duplicating data, depending on how you implement.
     
  9. Code (Text):
    public boolean isInLeague(Player member) {
        for (League league : leagues.keySet()) {
            if (league.hasMember(member)) {
                return true;
            }
        }
        return false;
    }
    Simply dont return false in the loop. I doubt a server is going to have hundreds of thousands of leagues, so speed/size should not be a concern. Iterating like this is fine.

    In fact, you could hit two birds with one stone here if you return the League the player is in instead of a boolean.

    Code (Text):
    public League isInLeague(Player member) {
        for (League league : leagues.keySet()) {
            if (league.hasMember(member)) {
                return league;
            }
        }
        return null;
    }
    If it returns null, the player is not in a league.

    Note, this only works if a player can only be in 1 league. If a player is in multiple leagues a random league will be returned as soon as it finds a match (Maps have no ordering). If a player can be in multiple leagues its time to go back to the drawing board for a new data structure design.
     
    #9 BillyGalbreath, Jun 2, 2016
    Last edited: Jun 2, 2016
  10. Thanks! A player can only be in one league. Otherwise it would be messed up.
     
  11. So this would work for adding a player to a guild (using commands)?
    Code (Text):
    Player invitee = Bukkit.getPlayerExact(args[0]);
                ManageLeagues ml = new ManageLeagues();
                if (!ml.isInLeague(invitee)) {
                    League league = ml.getLeague(p);
                    if (league.isLeader(p.getUniqueId()) || league.isOfficer(p)) {
                        league.addMember(invitee);
                    }
                }
     
  12. Wait isn't that just going to search through the first league? I mean it returns right away which breaks the loop. Or am I overlooking something? @avighnash did the same mistake earlier.
     
    • Agree Agree x 1
  13. Yes, and this is a limitation I covered already.

    Since the limitation doesnt effect this use-case (a player can only be in one league) it is an acceptable approach.
     
  14. I thought the use case is to find out if a player is in a league yet (because they are only allowed to join one). I don't see how your loop will solve the problem. If he is in league #2 your method will return false because he isn't in league #1.
     
    • Like Like x 1
  15. I dont think you really understand the purpose of a hashmap. Lets look at your code snippet from above:

    Code (Text):
     public League getLeague(String leagueName) {
            for (Map.Entry<League, String> e : leagues.entrySet()) {
                League league = e.getKey();
                if (e.getValue().equals(leagueName)) {
                    return league;
                }

            }
            return null;
        }
     
    Alright, you are iterating through all entries and check their values. However, a hashmap is supposed to be used as a key-value-storage. That means it should return an unique value for each unique key. So if you would swap Map<League, String> with Map<String, League>, we could replace your above method with following:
    Code (Text):
    return leagues.get(leagueName);
     
  16. Ok, I see what you are saying. Then yes, my second solution is the correct solution. I'll edit my post to reflect this ^_^ Good catch!
     
    • Agree Agree x 1
  17. Actually I can't remember seeing a loop with just a return statement that was ever a good idea^^
     
  18. Indeed. I'm shocked I didnt catch that earlier. :S Edited the post to provide two solutions again.