Java Map of Map - java

I need to do a look-up table based on two keys. I am building a mileage look-up chart similar to what is seen in the back of road maps. A sample of a chart can be found here. If you know the starting city is x and the ending city is y you look to find the intersection to find out the total miles.
When I first started attacking this problem I though of doing Two maps. City being an ENUM of my city of interest.
Map<City, Map<City, Integer>> map;
But, as I researched I am seeing warnings about Map's that have values of type Map. Is there an easier solution to my problem that I might be overlooking? With this being 66x66 col*row I want to make sure I do it right the first time and dont have to redo the data entry.
As a note I will be saving all my values into a database for easy update and retrieval so the solution would need to be easy to map with JPA or Hibernate etc.
Thanks in advanced.

It'd be easier if you do this:
Map<Pair<City, City>, Integer> map;
That is: create a new generic class, let's call it Pair that represents a pair of cities, and use it as key to your Map. Of course, don't forget to override hashCode() and equals() in Pair. And take a look at #increment1's answer, he's right: if the distance from city A to B is the same as the distance from B to A, then there's no need to store two pairs of cities, a single pair will do, no matter the order used to add the cities to the Map.
Notice that this is the strategy used by ORMs (for instance, JPA) when mapping composite keys in a database: create a new class (Pair in the example) that encapsulates all the objects used as keys, it'll be much easier to manage this way: conceptually, there's only one key - even if internally that key is composed of several elements.

Make a map of Path's, where Path is a custom class that holds two cities. Remember to override equals and hashcode.
Edit: Why is there 66x66 paths? Is the mileage different regarding which way you go (probably is a bit difference, but do you have that data)? If not, you can discard more than half that number of entries (the half is obvious, the 'more' part is from New York to New York entry no longer needs to be saved with 0).

You should create a simple class that contains two City references, from and to, and which overrides equals and hashCode appropriately. Then use that as your key.

Similar to other answers, I suggest creating a city pair class to be your map key (thus avoid a map of maps). One difference I would make, however, would be to make the city pair class order agnostic in regards to the cities in its hashCode and equals methods.
I.e. Make CityPair(Seattle,LA) equal to CityPair(LA,Seattle).
The advantage of this is that you would then not duplicate any unnecessary entries in your map automatically.
I would achieve this by having hashCode and equals always consider city1 to be the city with the lower ordinal value (via Enum.ordinal()) in your enum.
Alternatively, try this simple unordered pair implementation given in another question and answer.

If you're using Eclipse Collections, you can use MutableObjectIntMap and Pair.
MutableObjectIntMap<Pair<City, City>> map = ObjectIntHashMap.newMap();
map.put(Tuples.pair(newYorkCity, newark), 10);
map.put(Tuples.pair(newYorkCity, orlando), 1075);
Assert.assertEquals(10, map.get(Tuples.pair(newYorkCity, newark)));
Assert.assertEquals(1075, map.get(Tuples.pair(newYorkCity, orlando)));
Pair is built into the framework so you don't have to write your own. MutableObjectIntMap is similar to a Map<Object, Integer> but optimized for memory. It's backed by an Object array and an int array and thus avoids storing Integer wrapper objects.
Note: I am a committer for Eclipse collections.

To do the same as the graphic, i would use a 2d- array.
// index is the city code:
int[][] distances;
store the city code in a
Map<String, Integer> cityNameToCodeMap
Use it as follows;
Integer posA = cityNameTCodeMap.get("New York");
// TODO check posA and posB for null, if city does not exits
Integer posB = cityNameTCodeMap.get("Los Angeles");
int distance = distances[posA][posB];
reason for this design:
The matrix is in the graphic is not a sparse matrix, it is full.
For that case an 2d-array uses least memory.

There is another way to do this, that may work for you. Basically, you want to create a class called something like CityPair. It would take 2 arguments to its constructor, the start and end cities, and would override the hashcode function to generate a unique hash based on the two inputs. These two inputs could then be used in a HashMap<CityPair,Integer> type.
if there are only 66 cities, then your hashing function could look something like this:
//first assign each city an id, 0-65 and call it city.getID()
#Override public int hashCode()
{
return ((city1.getID() << 16) | (city2.getID()))
}
of course as noted in the comments, and in other answers, you will want to override the function prototyped by:
public boolean equals(Object)
from object so that the map can recover from a hash collision

Related

Does HashMap uses hashCode-equals methods of the HashMap or object?

I am confused regarding to the internal working of HashMap and as far as I understood, HashMap uses hashCode() and equals() methods of the given object that is stored in map (that's why the implementation of these methods are so important). On the other hand, the sources that I followed explains that as if HashMap uses its internal hashCode-equals methods to compare key values. Which one is right? Could you pls clarify me? I know the scenario of put / remove, but just need the place of the hashCode-equals methods. Are they in HashMap or Object?
In HashMap, Keys are hashed and compared, not the values. So, if you want to use your own objects as a Key, you need to implement hashCode and equals methods. e.g. You may want to store Student and corresponding Marksheet in a HashMap then for using Student objects as Key, override hashCode and equals method in that class.
If you just want to use String as key, then HashMap would call hashCode and equals method of String class
It uses it on the Object to facilitate key lookup. Here is an example.
Imagine you had 1000 files of individuals in a box and you had the ID number of one you had to find. So you need to go thru the box and search thru 1000 files to find the correct one.
Now imagine that you had two boxes. And all the even id's were in one box marked even and all the odd ones in a box marked odd. By looking at the ID you can pick the proper box (even or odd). So statistically you would only have to search thru about 500 files.
So the hashCode generates "boxes" based on the key.
And the search for the exact key within a box uses equals.
In my example, hashCode() could return 0 or 1 via something like the following:
#Override
public int hashCode() {
return id % 2; // returns 0 or 1 for even or odd box
}
Ideally a hashCode that generates many boxes (within reason) is best as the boxes become smaller (holds fewer objects). And a hashCode of a fixed value will work but it reduces the map to a single box with no benefit of improved performance.

Data structure to represent Teams and Players

There is a Team object , that contains list of players List<Players>. All teams need to be stored in a Teams collection.
Conditions:
If a new player need to be added to a particular team , that particular team is retrieved from Teams and Player need to be added to Players list of that team
Each Team object in the collection Teams need to be unique based on the team name
Team objects in the collection need to be sorted based on team name.
Considerations:
In this scenario when I use List<Team> , I can achieve 1, 3 . But uniqueness cannot be satisfied.
If I use TreeSet<Team> 2,3 can be achieved. But as there is no get method on TreeSet , a particular team cannot be selected
So I ended up using TreeMap<teamName,Team>. This makes all 1,2,3 possible. But I think it's not the good way to do it
Which Data Structure is ideal for this use case? Preferably form Java collections.
You can utilize your TreeSet if you wish. However, if you're going to utilize the Set interface you can use remove(object o) instead of get. You'll remove the object, make your modifications, then add it back into the set.
I think extending (i.e. creating a subclass from) ArrayList or LinkedList and overriding the set(), add(), addAll(), remove(), and removeRange() methods in such way that they ensure the uniqueness and sortedness conditions (invariants) would be a very clean design. You can also implement a binary search method in your class to quickly find a team with a given name.
ArrayList is a better choice to base your class on, if you aren't going to add or remove teams too frequently. ArrayList would give you O(n) insertion and removal, but O(log n) cost for element access and ensuring uniqueness if you use binary search (where n is the number of elements in the array).
See the generics tutorial for subclassing generics.
How about using a Guava's MultiMap? More precisely, a SetMultimap. Specifically, a SortedSetMultimap. Even more specifically, its TreeMultimap implementation (1).
Explanations:
In a MultiMap, a Key points not to a single value, but rather to a Collection of values.
This means you can bind to a single Team key a collection of several Player values, so that's Req1 solved.
In a SetMultiMap, the Keys are unique.
This gets your Req2 solved.
In a SortedSetMultimap, the Valuess are also sorted.
While you don't specifically care for this, it's nice to have.
In a TreeMultimap, The Keyset and each of their Values collections are Sorted.
This gets your Req3 sorted (See what I did there?)
Usage:
TreeMultimap<Team, Player> ownership = new TreeMultimap<Team, Player>();
ownership.put(team1, playerA);
ownership.put(team1, playerB);
ownership.put(team2, playerC);
Collection<Player> playersOfTeamA = ownership.get(team1); // contains playerA, playerB
SortedSet<Team> allTeams = ownership.keySet(); // contains team1, team2
Gothas:
Remember to set equals and hashCode correctly on your Team object to use its name.
Alternatively, you could use the static create(Comparator<? super K> keyComparator, Comparator<? super V> valueComparator) which provides a purpose-built comparison if you do not wish to change the natural ordering of Team. (use Ordering.natural() for the Player comparator to keep its natural ordering - another nice Guava thing). In any case, make sure it is compatible with equals!
MultiMaps are not Maps because puting a new value to a key does not remove the previously held value (that's the whole point), so make sure you understand it. (for instance it still hold that you cannot put a key-value pair twice...)
(1): I am unsure wether SortedSetMultimap is sufficient. In its Javadoc, it states the Values are sorted, but nothing is said of the keys. Does anyone know any better?
(2) I assure you, I'm not affiliated to Guava in any way. I just find it awesome!

Creating a map with a collection as the Key?

Is it possible to create a map where the key is a Collection (any sort of collection)?
If I try it on most common collections I'm told the collection can't be cast to comparable.
I've been trying to write a compareTo function for a custom collection, but I am struggling.
Either I need to write the compareTo, or I need to find a premade Map that accepts collections/Collection accepted by Maps.
How can I use a collection as a key on a map? I've looked over Stack overflow and I've googled this problem several times, but I've never found a solid solution!
The reason I want to do this is that I've written a 'shuffle' simulation in Java that mimic card shuffling. I want to be able to count up the number of times a specific hand (modeled as a collection) comes up. It would look something like this:
H4,C3,D2: 8
H9,D6,S11: 10
......
Is it possible to create a map where the key is a Collection (any sort of collection)?
Yes it is possible, but definitely not recommended. If your collection changes, it is likely that its hashcode will also change and that can lead to surprising behaviour.
See Map's javadoc:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
If I try it on most common collections I'm told the collection can't be cast to comparable.
The key does not need to be comparable, unless you use a sorted map, i.e. TreeMap. Use a simple HashMap and you won't have the issue.
Following your edit, I would create a new immutable Hand class:
class Hand implements Comparable<Hand> {
private final List<Card> cards;
Hand(Card c1, Card c2, Card c3) {
cards = Collections.unmodifiableList(Arrays.asList(c1, c2, c3));
}
//getters, no setters
//implement compareTo
}
and implement compareTo if you want to use it in a TreeSet<Hand, Integer> and sort by hand strength for example.
Yes, you can use any collection as a key. If you want a SortedMap like TreeMap you have to provide a Comparator to determine the order. However if you use any kind of HashMap you don't need.
Map<List<Integer>, String> map = new HashMap<>();
map.put(Arrays.asList(1,2,3), "one to three");
map.put(Arrays.asList(7,8,9), "seven eat nine");
System.out.println(map);
prints
{[1, 2, 3]=one to three, [7, 8, 9]=seven eat nine}

map vs ?? for storing name and score

I used hashmap to store data.
The problem is that I just noticed hashmap can't have more than one same key.
What else should I use to store data which the data looks like this:
Name1 100.0
Name2 99.8
Name3 121.5
...
Other thing I'm trying to do is to show data of one certain person, when I call that key.
So, is there way to store more than one value related to one key? or should I use other type of storage?
A hashmap can have duplicate keys if you store the values within another data structure such as a linked list or a tree at each key index. Then you just have to decide how to handle the collisions.
Edit:
HashMap
["firstKey"] => LinkedList of (3,4,5)
["secondKey"] => null
["thirdKey"] => LinkedList of (3)
To extend on Matthew Coxes answer, you could extend the Hashtable Class so that it automatically manages your lists for you and would give you the appearance of having multiple keys.
The Google guava library contain some collection type that allow for more that one element per key. The Multimap is the first one that come to mind.
http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimap.html
Guava in general contain a lot of very convenient utilities, I think its worth checking out.
If you can't use an external library, you can simply (Like Matthew Cox said) mix a map and a List with Map<K, List<V>>. But that is a bit more inconvenient to work with since you have to initialise a list for every key.
I'd rather go with my own datamodel and store that in a list, or map if you want fast access, e.g.
public class Player {
private String name;
private List<Float> scores;
}
The advantages:
you can easily see, what the structure wants to express
you can easily extend it (e.g. add aliases for the player, or calculate the avarage scor of player 1)

Difference between HashMap and ArrayList in Java?

In Java, ArrayList and HashMap are used as collections. But I couldn't understand in which situations we should use ArrayList and which times to use HashMap. What is the major difference between both of them?
You are asking specifically about ArrayList and HashMap, but I think to fully understand what is going on you have to understand the Collections framework. So an ArrayList implements the List interface and a HashMap implements the Map interface. So the real question is when do you want to use a List and when do you want to use a Map. This is where the Java API documentation helps a lot.
List:
An ordered collection (also known as a
sequence). The user of this interface
has precise control over where in the
list each element is inserted. The
user can access elements by their
integer index (position in the list),
and search for elements in the list.
Map:
An object that maps keys to values. A
map cannot contain duplicate keys;
each key can map to at most one value.
So as other answers have discussed, the list interface (ArrayList) is an ordered collection of objects that you access using an index, much like an array (well in the case of ArrayList, as the name suggests, it is just an array in the background, but a lot of the details of dealing with the array are handled for you). You would use an ArrayList when you want to keep things in sorted order (the order they are added, or indeed the position within the list that you specify when you add the object).
A Map on the other hand takes one object and uses that as a key (index) to another object (the value). So lets say you have objects which have unique IDs, and you know you are going to want to access these objects by ID at some point, the Map will make this very easy on you (and quicker/more efficient). The HashMap implementation uses the hash value of the key object to locate where it is stored, so there is no guarentee of the order of the values anymore. There are however other classes in the Java API that can provide this, e.g. LinkedHashMap, which as well as using a hash table to store the key/value pairs, also maintains a List (LinkedList) of the keys in the order they were added, so you can always access the items again in the order they were added (if needed).
If you use an ArrayList, you have to access the elements with an index (int type). With a HashMap, you can access them by an index of another type (for example, a String)
HashMap<String, Book> books = new HashMap<String, Book>();
// String is the type of the index (the key)
// and Book is the type of the elements (the values)
// Like with an arraylist: ArrayList<Book> books = ...;
// Now you have to store the elements with a string key:
books.put("Harry Potter III", new Book("JK Rownling", 456, "Harry Potter"));
// Now you can access the elements by using a String index
Book book = books.get("Harry Potter III");
This is impossible (or much more difficult) with an ArrayList. The only good way to access elements in an ArrayList is by getting the elements by their index-number.
So, this means that with a HashMap you can use every type of key you want.
Another helpful example is in a game: you have a set of images, and you want to flip them. So, you write a image-flip method, and then store the flipped results:
HashMap<BufferedImage, BufferedImage> flipped = new HashMap<BufferedImage, BufferedImage>();
BufferedImage player = ...; // On this image the player walks to the left.
BufferedImage flippedPlayer = flip(player); // On this image the player walks to the right.
flipped.put(player, flippedPlayer);
// Now you can access the flipped instance by doing this:
flipped.get(player);
You flipped player once, and then store it. You can access a BufferedImage with a BufferedImage as key-type for the HashMap.
I hope you understand my second example.
Not really a Java specific question. It seems you need a "primer" on data structures. Try googling "What data structure should you use"
Try this link http://www.devx.com/tips/Tip/14639
From the link :
Following are some tips for matching the most commonly used data structures with particular needs.
When to use a Hashtable?
A hashtable, or similar data structures, are good candidates if the stored data is to be accessed in the form of key-value pairs. For instance, if you were fetching the name of an employee, the result can be returned in the form of a hashtable as a (name, value) pair. However, if you were to return names of multiple employees, returning a hashtable directly would not be a good idea. Remember that the keys have to be unique or your previous value(s) will get overwritten.
When to use a List or Vector?
This is a good option when you desire sequential or even random access. Also, if data size is unknown initially, and/or is going to grow dynamically, it would be appropriate to use a List or Vector. For instance, to store the results of a JDBC ResultSet, you can use the java.util.LinkedList. Whereas, if you are looking for a resizable array, use the java.util.ArrayList class.
When to use Arrays?
Never underestimate arrays. Most of the time, when we have to use a list of objects, we tend to think about using vectors or lists. However, if the size of collection is already known and is not going to change, an array can be considered as the potential data structure. It's faster to access elements of an array than a vector or a list. That's obvious, because all you need is an index. There's no overhead of an additional get method call.
4.Combinations
Sometimes, it may be best to use a combination of the above approaches. For example, you could use a list of hashtables to suit a particular need.
Set Classes
And from JDK 1.2 onwards, you also have set classes like java.util.TreeSet, which is useful for sorted sets that do not have duplicates. One of the best things about these classes is they all abide by certain interface so that you don't really have to worry about the specifics. For e.g., take a look at the following code.
// ...
List list = new ArrayList();
list.add(
Use a list for an ordered collection of just values. For example, you might have a list of files to process.
Use a map for a (usually unordered) mapping from key to value. For example, you might have a map from a user ID to the details of that user, so you can efficiently find the details given just the ID. (You could implement the Map interface by just storing a list of keys and a list of values, but generally there'll be a more efficient implementation - HashMap uses a hash table internally to get amortised O(1) key lookup, for example.)
A Map vs a List.
In a Map, you have key/value pairs. To access a value you need to know the key. There is a relationship that exists between the key and the value that persists and is not arbitrary. They are related somehow. Example: A persons DNA is unique (the key) and a persons name (the value) or a persons SSN (the key) and a persons name (the value) there is a strong relationship.
In a List, all you have are values (a persons name), and to access it you need to know its position in the list (index) to access it. But there is no permanent relationship between the position of the value in the list and its index, it is arbitrary.

Categories