Reputation: 1983
If I have a hashmap say HashMap<Integer, String> map = new HashMap<>();
If I have all the values e.g 1 up to 100 all storing the same object. In memory, will this be 100 instances of that object or 100 pointers to one object.
Why?
Well if you have a map with HashMap<String, Integer>
(notice the swap in generics) and the string is a word and the integer is the number of occurrences if I need to pick a word at random however such that it is proportional to the number of its occurences then a quick way would be just to fill an arraylist with the word "cat" 100 times and the rest accordingly (to "convert" the hashmap into an arraylist) and that way when a random number is picked using list.get(i) then its proportional to its occurences.
So this will take as n words * m occurences which means a huge list. So how efficient would it be to use a HashMap instead.
If indeed there will be pointers from the key to the value (when they repeat) then surely the map is a better approach.
Upvotes: 3
Views: 1436
Reputation: 393866
It seems to me that the two options you are considering are either :
List<String> words = new ArrayList<>();
words.add("cat");
words.add("cat");
...
words.add("cat");
vs.
Map<Integer,String> words = new HashMap<>();
words.put(0,"cat");
words.put(1,"cat");
...
words.put(99,"cat");
Both the List
and the Map
would contain multiple reference to the same String object ("cat"). The Map
would require more memory, though, since it also has to store the keys.
In addition, you have no easy way of obtaining the i
'th String
value of the Map
for a given random i
, since HashMap
has no order.
Therefore your List
solution is preferable to your suggested Map<Integer,String>
alternative.
That said, you could build a more efficient TreeMap
that would allow you to get a random String
depending on its number of occurrences.
One way I can think of :
TreeMap<Integer,String> map = new TreeMap<>();
map.put(0,"cat");
map.put(100,"dog");
This TreeMap
represents 100 occurrences of "cat" and 20 occurrences of "dog". Now, if you draw a random number from 0 to 119, you can easily check whether it lands in the range of "cat" or "dog".
For example, if you draw the number 105, you obtain the corresponding String
with :
String randomStr = map.ceilingEntry(105).getValue();
All that remains is to convert the HashMap<String, Integer>
containing the number of occurrences to the corresponding TreeMap<Integer, String>
:
HashMap<String, Integer> occurrences = ...
TreeMap<Integer, String> map = new TreeMap<>();
int count = 0;
for (Map.Entry<String,Integer> entry : occurrences.entrySet()) {
map.put (count, entry.getKey());
count += entry.getValue();
}
Note that I'm using TreeMap
instead of HashMap
in order to be able to efficiently obtain the entry having the least key greater than or equal to the given key (without having to iterate over all the entries). This is only possible in NavigableMap
s.
Upvotes: 1
Reputation: 48288
After looking in the Map implemetation, Map#put() uses the static class Node<K,V>
which is handling references
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
example:
final Map<Integer, Point> map = new HashMap<>();
final Point xPoint = new Point(0, 0);
map.put(1, xPoint);
map.put(2, xPoint);
map.put(3, xPoint);
System.out.println(map);
// modify the point
System.out.println(xPoint);
xPoint.setX(555);
System.out.println(xPoint);
System.out.println(map);
and I gave a try defininf a custom MAp Integer, Point, (custom point)
Map<Integer, Point> map = new HashMap<>();
Point xPoint = new Point(0, 0);
map.put(1, xPoint);
map.put(2, xPoint);
map.put(3, xPoint);
System.out.println(map);
// modify the point
System.out.println(xPoint);
xPoint.setX(555);
System.out.println(xPoint);
System.out.println(map);
as you can see, modifing the point will affect the hole map, since all nodes.V are pointing to the same ref.
Upvotes: 2