I\'m working with a large ArrayList
, and I would repeatedly need to select a random key from a random HashMap (and do some stuff with it).
I'm assuming you are using HashMap
as you need to look something up at a later date?
If not the case, then just change your HashMap
to an Array
/ArrayList
.
If this is the case, why not store your objects in a Map
AND an ArrayList
so you can look up randomly or by key.
Alternatively, could you use a TreeMap
instead of HashMap
? I don't know what type your key is but you use TreeMap.floorKey()
in conjunction with some key randomizer.
You need access to the underlying entry table.
// defined staticly
Field table = HashMap.class.getDeclaredField("table");
table.setAccessible(true);
Random rand = new Random();
public Entry randomEntry(HashMap map) {
Entry[] entries = (Entry[]) table.get(map);
int start = rand.nextInt(entries.length);
for(int i=0;i<entries.length;i++) {
int idx = (start + i) % entries.length;
Entry entry = entries[idx];
if (entry != null) return entry;
}
return null;
}
This still has to traverse the entries to find one which is there so the worst case is O(n) but the typical behaviour is O(1).
from top of my head
List<A> keysAsArray = new ArrayList<A>(map.keySet())
Random r = new Random()
then just
map.get(keysAsArray.get(r.nextInt(keysAsArray.size()))
If you absolutely need to access the Entry array in HashMap, you can use reflection. But then your program will be dependent on that concrete implementation of HashMap.
As proposed, you can keep a separate list of keys for each map. You would not keep deep copies of the keys, so the actual memory denormalisation wouldn't be that big.
Third approach is to implement your own Map implementation, the one that keeps keys in a list instead of a set.
Sounds like you should consider either an ancillary List of keys or a real object, not a Map, to store in your list.
After spending some time, I came to the conclusion that you need to create a model which can be backed by a List<Map<A, B>>
and a List<A>
to maintain your keys. You need to keep the access of your List<Map<A, B>>
and List<A>
, just provide the operations/methods to the caller. In this way, you will have the full control over implementation, and the actual objects will be safer from external changes.
Btw, your questions lead me to,
values()
method also returns Set
.This example, IndexedSet, may give you an idea about how-to.
[edited]
This class, SetUniqueList, might help you if you decide to create your own model. It explicitly states that it wraps the list
, not copies. So, I think, we can do something like,
List<A> list = new ArrayList(map.keySet());
SetUniqueList unikList = new SetUniqueList(list, map.keySet);
// Now unikList should reflect all the changes to the map keys
...
// Then you can do
unikList.get(i);
Note: I didn't try this myself. Will do that later (rushing to home).