How to select a random key from a HashMap in Java?

前端 未结 9 1120
小鲜肉
小鲜肉 2021-02-05 05:03

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).

相关标签:
9条回答
  • 2021-02-05 05:45

    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.

    0 讨论(0)
  • 2021-02-05 05:47

    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).

    0 讨论(0)
  • 2021-02-05 05:48

    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()))
    
    0 讨论(0)
  • 2021-02-05 05:52

    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.

    0 讨论(0)
  • 2021-02-05 05:56

    Sounds like you should consider either an ancillary List of keys or a real object, not a Map, to store in your list.

    0 讨论(0)
  • 2021-02-05 05:59

    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,

    • Why does the java.util.Set<V> interface not provide a get(Object o) method?, and
    • Bimap: I was trying to be clever but, of course, its 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).

    0 讨论(0)
提交回复
热议问题