HashMap with multiple valued keys - java

Is it possible to create a HashMap whose key is an array of integers?
I´m accustomed to use Python and just recently I began working with Java. In my job I need to create a HashMap with keys like:
map.put([5,2], 1);
map.put([3,2], 0);
and so on. I´m using it to later on test if a pair of those numbers are present in the map, if yes, then do something, if not, continue.
To do so I tried the following:
Map<Array, Boolean> test = new HashMap<Array, Boolean>();
int[] a1 = {5,2};
test.put(a1, true);
Eclipse gives the message ("The arguments are not applicable to int[]..."). But any configuration I´ve done I get some error.
I tried using ArrayList, Objects inside the map, nested HashMap and so on but none worked (in python it´s very easy, I´d just write dict[(5,2)] = 1, so I imagine in Java there´s something simple like that). I was suggested to transform the numbers into Strings and add a colon between then, like:
map.put("5:2", 1);
and later on I break the string again but if this is the solution I´ll go back to Python ;)!!
Probably this is a very simple question but I couldn´t find the answer, hope you can help me.
Thanks in advance!

If you want to check for the existance of your entry, you can use a Set (a useful concrete implementation is HashSet.
final Set<List<Integer>> population;
population = new HashSet<List<Integer>>();
population.add(Arrays.asList(1, 2));
if (population.contains(Arrays.asList(1, 2)))
{
...
}
You can use an List as I have done above - but that doesn't guarantee that all your lists are exactly two elements long (if that is indeed a constraint). To make it a bit more robust, you could create your own class to represent the tuple. If you do, make sure you implement equals() and hashCode() (here's an article explaining good practice).
Arrays.asList() is a useful way of creating a list in-line in the code. A more general list is an ArrayList.

The simplest thing would be a Map<List<Integer>, Boolean> -- or even just a Set<List<Integer>>, since you don't care about the value as much as whether the key is there.
The more java-y solution would be some class that represents the two ints:
public class Coordinate { // or whatever
private final int x;
private final int y;
// constructor and overrides for equals, hashCode and toString
}
Then have a Set<Coordinate>.
This is considered more idiomatically Java because the class name tells you exactly what this set is for -- and enforces that it's used that way. A Set<List<Integer>>, on the other hand, could be lots of things: coordinates, lottery pickings, SSNs for people in a given department, credit cards for a payment... you the programmer have no way of knowing just by looking at the type, and it's easy for a set of numbers that means on thing to be accidentally used in another context. A Set<Coordinate> can't be anything other than a set of coordinates.

This works:
Map<List<Integer>,Boolean> map = new HashMap<>();
map.put( Arrays.asList( new Integer(1), new Integer(2) ), false );
map.put( Arrays.asList( 4, 5 ), true ); // Integer type is inferred and ints are autoboxed
map.get( Arrays.asList( 1, 2 ) ); // gets the Boolean object for "false"

Actually java is a lot less expressive than python so you'll have to write a bit more code.
I think you need to use a map with an ArrayList in the following way:
ArrayList al = map.get("key");
if (al == null) {
al = new ArrayList();
map.put("key", al)
}
al.add(some_object);
You also may use the array as the key (as you asked for), probably you want an immutable array. The key for the hash map to work is use some object that has a good implementation of equals and hashCode.
This is the way that it is done in java, you may use any kind of collection though, Sets are more common.
Cheers!

Related

Java Hashmap containsKey returns false for existing key

There is this problem in my code I'm trying to figure out the source of the problem, It seems quite deep and I can't figure it out. I have some really major clues, yet I feel something is missing in my understanding so I can't figure out how to fix it.
In my code, i'm calling a constructor from a different class of an object with a field of a Hashmap<Integer[], Integer[]> and when I debug I see that the map does contain the key I'm looking for. Here map contains one mapping which maps the array {0} to the array {0}:
Integer[] key = {0};
boolean contains = obj.map.containsKey(key); // is false in the debugger
Though If I initialize a new Hashmap it behaves differently:
Hashmap<Integer[], Integer[]> map = new Hashmap();
Integer[] key = {0};
Integer[] val = {0};
map.put(key,val);
boolean contains = map.containsKey(key); // is true in the debugger
I see one clear difference between the cases, that in the second case, the variable "key" and the actual key in the Hashmap are the same object, though in the first they are somehow not the same object. I guess this is what causes the difference. But I can't seem to get how can I control the key always being the same object in the map, or how should I implement this such that keys will always be found. I would use int instead of Integer but this is not allowed in Hashmap so how should be the correct workflow in such a case?
P.S I hope the code is clear enough because it is a hell lot of code to add here to get the entire thing running. If it is necessary I will try to compose a new, more clear, and short code to demonstrate the problem, though I am hoping it can be explained without it.
Thanks
Arrays are not suitable keys for HashMaps, since arrays don't override equals() and hashCode() of Object class.
As a result, two arrays that have the exact same elements are not considered equal to each other by HashMap.
You can use List<Integer> instead of Integer[] as the key of your HashMap, since Lists do override equals() and hashCode() (and so does Integer).

How to randomly select key with specific value from hashmap in as little code as possible?

This isnt a question about hashmaps. This is a question about shorthand java notation.
I have a HashMap. This hashmap only every contains the keys 'n','e','s','w'. What i would like to do is randomly select a key from this hashmap but only if its corresponding value is true.
I have written a function to do this for me and it works exactly how i need it to. Now i would like to condense my code as much as absolutely possible, while still maintaining the same exact functionality.
First thing i'll show you is how i create my hashmap for context:
HashMap<String, Boolean> potentialMoves2 = new HashMap<String, Boolean>();
potentialMoves2.put("n", true);
potentialMoves2.put("e", true);
potentialMoves2.put("s", true);
potentialMoves2.put("w", true);
Next is the code in question:
private void randomlyMoveCharacter(double x, double y, Character character){
List<String> myArrayList = new ArrayList<String>();
for(Map.Entry<String,Boolean> mySet : character.potentialMoves2.entrySet()){
if(mySet.getValue().equals(true)){
myArrayList.add(mySet.getKey());
}
}
String direction = myArrayList.get(new Random().nextInt(myArrayList.size()));
if(direction.equals("n")){
character.mapY++;
character.hasMoved = true;
}
if(direction.equals("e")){
character.mapX++;
character.hasMoved = true;
}
if(direction.equals("s")){
character.mapY--;
character.hasMoved = true;
}
if(direction.equals("w")){
character.mapX--;
character.hasMoved = true;
}
}
I'm pretty convinced that a good portion of this functionality can be written in shorthand in one line of code. I know at the very very least all of the if statements can be rewritten to be shorter. It would be ideal to, rather than have a strongly defined ArrayList object, to rather have a temporary Array[] which is never initialized. By this i mean performing computations on the code that calls the "toArray()" function.
A Side Note
I know for a fact that there is a better way to do what i want to do. Maybe using a Hash Map is a poor idea. Maybe using strings in general is dumb. I am fully aware that there is a more efficient way to do this. That being said, i am open to suggestions; however, i would still like to know how to write my code with less lines regardless.
Wrong data structure. You just need a Set of currently legal moves. I would also make the directions an Enum instead of a String, which makes the Set an EnumSet, and use a switch statement instead of the if chain.

Java Map of Map

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

Get values for keys within a range in Java

Suppose I have a map in Java which looks like this:
{
39:"39 to 41",
41:"41 to 43",
43:"43 to 45",
45:">=45"
}
If the keys are in sorted order(either using treemap or linkedhashmap).Now if i try to get a value which is >=39 and <41.Then I should get the String "39 to 41".How do I do this efficiently?
It looks like you want more than a SortedMap; you want a NavigableMap! Specifically you can use the floorKey operation.
Here's an example:
NavigableMap<Integer,String> map =
new TreeMap<Integer, String>();
map.put(0, "Kid");
map.put(11, "Teens");
map.put(20, "Twenties");
map.put(30, "Thirties");
map.put(40, "Forties");
map.put(50, "Senior");
map.put(100, "OMG OMG OMG!");
System.out.println(map.get(map.floorKey(13))); // Teens
System.out.println(map.get(map.floorKey(29))); // Twenties
System.out.println(map.get(map.floorKey(30))); // Thirties
System.out.println(map.floorEntry(42).getValue()); // Forties
System.out.println(map.get(map.floorKey(666))); // OMG OMG OMG!
Note that there are also ceilingKey, lowerKey, higherKey, and also …Entry instead of …Key operations as well which returns a Map.Entry<K,V> instead of just the K.
Try Java 6 java.util.NavigableMap. http://download.oracle.com/javase/6/docs/api/java/util/NavigableMap.html.
In special use floorKey/floorEntry.
By example: floorKey(40) should return 39. floorEntry would return the value you are looking for.
With a sorted map, you could do something like that:
SortedMap<Integer,String> head = map.headMap(value+1);
if (head.isEmpty()) {
return null;
} else {
return head.get(head.lastKey());
}
I'm not sure that's going to be easy. One suggestion would be to "fill in the gaps", ie put in a value 40->"39 to 41" etc etc. I suppose that will only be possible if you know the whole range of numbers possible in the map.
Or mabybe something that overrides the get to check to see if the value is in the map, and expanding out until it finds something. I'm not sure that's going to be possible in its current guise, as you'd have to end up parsing the value strings.
You can recursively look for lower boundary.
public String descriptionFor(int value) {
String description = map.get(value);
return description == null ? descriptionFor(value--) : description;
}
You will need to have a minimum boundary.
You'd have to implement such a map yourself, I believe. You're right that it would have to be sorted; the implementation of get would have to iterate through the keys until it finds the largest key that is less than or equal to the argument.
If you subclass TreeMap it would initially appear that you can get this working via simply overriding the get() method. However, to maintain as much of the Map contract as possible you'll have to override other methods for consistency.
And what about e.g. containsKey()? Does your main contain a mapping for 40? If you return false, then a client can decide not to call get() based on this information; for these reason (and the formal definition) you have to return true. But then it makes it hard to determine whether the map "really contains" a given mapping; if you're looking to do something such as update without overwriting anything that already exists.
The remove() method might be tricky too. From my reading of the interface,
// Calling map.remove "Removes the mapping for a key from this map if it is present."
map.remove(x);
// Now that the mapping is removed, I believe the following must hold
assert map.get(x) == null;
assert map.containsKey(x);
Acting consistently here would be very tricky. If you have a mapping from 35-40 for example, and you call remove(38), then as I understand it you'd have to return null for any subsequent gets for the key 38, but return the aforementioned mapping for keys 35-37 or 39-40.
So while you can make a start on this by overriding TreeMap, perhaps the whole concept of Map is not quite what you want here. Unless you need this behaviour to slot into existing methods that take Map, it might be easier to create it yourself as a distinct class since it's not quite a Map, the way you're defining it.

Java - remove last known item from HASHMAP on MAP!s

OK so this is a BIT different. I have a new HashMap
private Map<String, Player> players = new HashMap<String, Player>();
How do I remove last known item from that? Maybe somethign like this?
hey = Player.get(players.size() - 1);
Player.remove(hey);
The problem is, a HashMap is not sorted like a list. The internal order depends on the hashCode() value of the key (e.g. String). You can use a LinkedHashMap which preserves the insert order. To remove the last entry on this you can use an iterator in combination with a counter which compares to the size and remove the last entry.
It's so easy. Try this:
Map<String, Player> players = new LinkedHashMap<String, Players>();
List<String> list = new ArrayList<String>(players.keySet());
map.remove(list.get(list.size()-1));
I'm a little bit confused. First of all, you're saying that you've got a new ArrayList and you're illustrating this with a line that creates a new HashMap. Secondly, does the Player class really have static methods like get(int) and remove(Object)?
HashMap doesn't have a particular order, ArrayList (as any other List) does.
Removing from an ArrayList
If you've got a list of players, then you can do the following:
private List<Player> players = new ArrayList<Player>();
// Populate the list of players
players.remove(players.size() - 1);
Here, I've used the remove(int) method of List, which allows to remove an item at an arbitrary index.
Removing from a HashMap
If you've got a map of players, there's no such thing as "the last item". Sure, you can iterate over the map and one of the items will pop out last, but that doesn't mean anything. Therefore, first you have to find out what you want to remove. Then you can do the following:
private Map<String, Player> players = new HashMap<String, Player>();
// Populate the map of players
// Find the key of the player to remove
players.remove(toRemove);
Here, I've used the remove(Object) method of Map. Note that in order to remove some key-value pair, you have to show the key, not the value.
There's no "first" and "last" in a HashMap. It's unordered. Everything is accessible by its key, not by index.
You cannot delete from HashMap like that. You need to use LinkedHashMap.
Simple, just do something of this effect.
1) Get a keyset iterator;
2) Create a Key somelastKey = null
3) Iterate through the iterator and assigning somelastKey until iterator finishes.
4) finally, do players.remove(somelastKey);
Bear in mind that HashMap is unordered, it depends on Object's hashCode to determine insertion order.
Instead of using HashMap, try using LinkedHashMap which keeps a predictable iteration order.
Hope this helps....
You'll probably have to extend HashMap, override put so that it caches the key, and then create a new method that just removes the key that was cached.
Unfortunately, this will only let you remove the most recently added. If you need to remove the most recently added multiple times (without inserting in-between the removes), you're out of luck.
In that case, I'd probably do the same overrides, just write the keys to a List. So you'd have both a list and a Map.
When adding:
String key; Player value;
lastKey = key;
map.put(key, value);
//...later...
Player lastAdded = map.remove(lastKey);
Other than that there's really no way without using a LinkedHashMap or in some way creating your own wrapper map or extending HashMap.
You shouldn't be using a raw hashmap anywhere because things like this happen.
Get in the habit of wrapping your collections in business logic classes.
See, in your case right now you need to associate these two related variables--your hashmap and a "Last entered" item so you can remove it.
If you need to remove the last item from some other class, you need to pass both items.
Any time you find yourself passing 2 or more items together into more than one API, you are probably missing a class.
Create a new class that contains the hashmap and a "lastAdded" variable. Have put and remove methods that are just forwarded to the hashmap, but the put method would also set the lastAdded variable.
Also be sure to add a removeLast() method.
NEVER allow access to your hashmap outside this class, it needs to be completely private (this is what I mean by wrapped). In this way you can ensure it doesn't get out of sync with the lastAdded variable (also completely private).
Just to reiterate getters and setters for these variables would be a terrible idea (as they are with nearly all actual OO code).
You will quickly find a bunch of other methods that NEED to be in this class in order to access data inside your hashmap--methods that never felt right in their current location. You will probably also notice that those methods always have an additional parameter or two passed in--those parameters should probably be members of your new class.
Once you get in the habit of doing actual OO design (via refactoring in this case), you'll find your code MUCH more manageable. To illustrate this point, if you find later that you need multiple levels of "delete last", it will be TRIVIAL to add to your class because it will be extremely clear exactly what methods can modify your hashtable and where your new "stack" of lastItems should be located--in fact it's probably a 2 line code change.
If you do not make this wrapper class, various locations will each have code to set "lastAdded" when they add code to the hashtable. Each of those locations will have to be modified, some may be in other classes requiring you to pass your new stack around with the hashtable. It will be easier to get them out of synch if you forget to change one location.

Categories