2

Let's say that I had reasons to require a fast lookup of a class instance by multiple value types, for the sake of this explanation I'm going to be using a game server as an example.

Lets say that the server handles users with a static identification number. This number is used to communicate and interact with specific players (Ie: Private Chatting, Trade requests, Combat, Guild invites, and so forth).

This would require the frequent usage of looking the player up by their identification number, based on my current experience the best way to do this is like so: (Please correct me if I'm wrong.)

HashMap<Integer, Player>

However when dealing with networking, there's a lot of times when I'm going to also need to get the player associated with the network session, or "socket" as some people may be more familiar with. Which would look something like so:

HashMap<Connection, Player>

So what I'm trying to figure out is, should I go this route:

HashMap<Integer, Player> playersById;
HashMap<Connection, Player> playersByConnection;

or should I do something a little more "Smashed together" like so:

HashMap<Object[], Player> playersOnline;

and have Object[0] as the Integer, and Object[1] as the Connection, then use the one required during lookup.

Or are both of these ways inneficient and incorrect, is there a better/faster way to look them up by either Integer or Connection without duplicating the collections?

Any insight would be GREATLY appreciated.

edit: Also, is there anything against having a HashSet<> and a HashMap<> containing the same class references? I've noticed that the HashSet<> is far more efficient at iterating than the HashMap<> and have been keeping a Map for lookup and a Set for iterating, is this bad practice?

Hobbyist
  • 15,888
  • 9
  • 46
  • 98
  • @aliteralmind - Thanks for the link, already read it all though! – Hobbyist Feb 12 '15 at 04:23
  • Have you quantified the performance difference between iterating HashSet and HashMap? And what you keep in your HashSet? "Player" Object? – Hamed Moghaddam Feb 12 '15 at 04:25
  • 1
    @aliteralmind Marked as a duplicate? Really? This question was about whether it's a good idea to have multi-domain keys to a map: there was only a minor addendum about choosing implementation. The 'duplicate' was about an easy way to choose which collection to use: only vaguely related to this question. – sprinter Feb 12 '15 at 05:02
  • @sprinter: It was marked as duplicate by user Jarrod Roberson. – aliteralmind Feb 12 '15 at 11:57
  • @aliteralmind ah ok thanks - I'll address the question to him – sprinter Feb 12 '15 at 20:06
  • @sprinter (And for what it's worth, I agree with you.) – aliteralmind Feb 13 '15 at 00:35

3 Answers3

1

I would certainly recommend that you have separate maps for your two different searches. They are really quite different and independent needs. You also might later need to add new ways to look up players (by name, or by location, or game instances). You don't want to have to come back and keep changing existing working data structures.

My suggestion would be to encapsulate both of your search maps inside the classes holding lists of players or connections. That way they become just an internal implementation detail in those classes not something that the Player class (for example) needs to worry about.

So, for example:

class PlayerPopulation {
    private final List<Player> playerList = new ArrayList<>();
    private final Map<Player.ID, Player> playerByID = new HashMap<>();

    public void addPlayer(Player player) {
        playerList.add(player);
        playerByID.put(player.getID(), player);
    }

    public Player getPlayerByID(Player.ID id) {
        return playerByID.get(id);
    }
}

The same pattern would be used for ConnectionPool (or whatever your connection container is called). That way you can easily add new ways of searching for players without any other classes having to worry about the map structure you are using. You can also trivially convert to a HashSet or anything else without anything being impacted outside the one class. You can't do that if you are trying to make a map support multiple search paths.

I also changed ID to an inner class rather than assuming Integer. I realise you were just giving an example but thought it was another example of good encapsulation: you could change to a Long without changing the PlayerPopulation class at all.

So yes I definitely recommend against mashing your search keys together.

sprinter
  • 27,148
  • 6
  • 47
  • 78
-1

First of all, I must mention the invaluable What Collection should I use flowchart.


Absolutely use the two HashMap/non-"smashed" version for two independent maps, then abstract it away in a higher level class that can be as smashed as you like--such as with static utility functions or a more helpful "usage" object.

Your second ("smashed") version is attempting to abstract away this dual lookup. But you need two fast and independent lookups. Don't force the abstraction that you need/want at the application level onto the data level.

Or are both of these ways inneficient and incorrect, is there a better/faster way to look them up by either Integer or Connection without duplicating the collections?

Duplicating collections can be a good thing or a bad thing. If it makes your code more elegant and understandable, it's good.

Premature optimization is the root of all evil.

Also, is there anything against having a HashSet<> and a HashMap<> containing the same class references?

I've noticed that the HashSet<> is far more efficient at iterating than the HashMap<> and have been keeping a Map for lookup and a Set for iterating, is this bad practice?

Nothing wrong at all with doing any of these things as long as it makes your code more elegant and understandable.

Community
  • 1
  • 1
aliteralmind
  • 19,847
  • 17
  • 77
  • 108
-1

Create a wrapper object and include those two objects in it. Don't forget to override the hashMap() and equals() methods when creating the object because you are planning to use it as a key in a Map. Calculate the hasCode based on the two objects hashCode and evaluate the equals also based on the containing instances. See this answer: equals and hashcode

Community
  • 1
  • 1