views:

1005

answers:

8

Is there a elegant way of obtaining only one Entry<K,V> from HashMap, without iterating, if key is not known.

As order of entry of entry is not important, can we say something like

hashMapObject.get(zeroth_index);

Although I am aware that there exist no such get by index method.

If I tried approach mentioned below, it would still have to get all the entry set of the hashmap.

for(Map.Entry<String, String> entry : MapObj.entrySet()) {
    return entry;
}

Suggestions are welcome.

EDIT: Please suggest any other Data Structure to suffice requirement.

+3  A: 

Maps are not ordered, so there is no such thing as 'the first entry', and that's also why there is no get-by-index method on Map (or HashMap).

You could do this:

Map<String, String> map = ...;  // wherever you get this from

// Get the first entry that the iterator returns
Map.Entry<String, String> entry = map.entrySet().iterator().next();

(Note: Checking for an empty map omitted).

Your code doesn't get all the entries in the map, it returns immediately (and breaks out of the loop) with the first entry that's found.

Note: Calling iterator() does not mean that you are iterating over the whole map.

Jesper
+2  A: 

I guess the iterator may be the simplest solution.

return hashMapObject.entrySet().iterator().next();

Another solution (not pretty):

return new ArrayList(hashMapObject.entrySet()).get(0);

Or yet (not better):

return hashMapObject.entrySet().toArray()[0];
cadrian
There's no reason to use the second or third version. The first one is perfectly fine, the other two waste a lot of time by providing an array/List for nothing.
Joachim Sauer
I agree, hence the "not pretty" comments ;-)
cadrian
+2  A: 

What do you mean with "without iterating"?

You can use map.entrySet().iterator().next() and you wouldn't iterate through map (in the meaning of "touching each object"). You can't get hold of an Entry<K, V> without using an iterator though. The Javadoc of Map.Entry says:

The Map.entrySet method returns a collection-view of the map, whose elements are of this class. The only way to obtain a reference to a map entry is from the iterator of this collection-view. These Map.Entry objects are valid only for the duration of the iteration.

Can you explain in more detail, what you are trying to accomplish? If you want to handle objects first, that match a specific criterion (like "have a particular key") and fall back to the remaining objects otherwise, then look at a PriorityQueue. It will order your objects based on natural order or a custom-defined Comparator that you provide.

janko
+3  A: 

Why do you want to avoid calling entrySet() it does not generally create an entirely new object with its own context, but instead just provide a facade object. Simply speaking entrySet() is a pretty cheap operation.

Joachim Sauer
+5  A: 

The answer by Jesper is good. An other solution is to use TreeMap (you asked for other data structures).

TreeMap<String, String> myMap = new TreeMap<String, String>();
String first = myMap.firstEntry().getValue();
String firstOther = myMap.get(myMap.firstKey());

TreeMap has an overhead so HashMap is faster, but just as an example of an alternative solution.

Per Östlund
Or ConcurrentSkipListMap
Stephen Denne
For some reason, the `firstEntry()` method is not defined in the interface `SortedMap` but only in `TreeMap` and `ConcurrentSkipListMap` :(
janko
firstEntry() is defined in the NavigableMap interface.
Per Östlund
Ah, thanks, I only looked at `SortedMap` because it had the `firstKey()` method.
janko
A: 

Following your EDIT, here's my suggestion :

If you have only one entry, you might replace the Map by a dual object. Depending on the types, and your preferences:

  • an array (of 2 values, key and value)
  • a simple object with two properties
KLE
A: 

This would get a single entry from the map, which about as close as one can get, given 'first' doesn't really apply.

import java.util.*;

public class Friday {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<String, Integer>();

        map.put("code", 10);
        map.put("to", 11);
        map.put("joy", 12);

        if (! map.isEmpty()) {
            Map.Entry<String, Integer> entry = map.entrySet().iterator().next();
            System.out.println(entry);
        }
    }
}
Michael Easter
A: 

If you really want the API you suggested, you could subclass HashMap and keep track of the keys in a List for example. Don't see the point in this really, but it give you what you want. If you explain the intended use case, maybe we can come up with a better solution.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SuppressWarnings("unchecked")
public class IndexedMap extends HashMap {

    private List<Object> keyIndex;

    public IndexedMap() {
     keyIndex = new ArrayList<Object>();
    }

    /**
     * Returns the key at the specified position in this Map's keyIndex.
     * 
     * @param index
     *            index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >= size())
     */
    public Object get(int index) {
     return keyIndex.get(index);
    }

    @Override
    public Object put(Object key, Object value) {

     addKeyToIndex(key);
     return super.put(key, value);
    }

    @Override
    public void putAll(Map source) {

     for (Object key : source.keySet()) {
      addKeyToIndex(key);
     }
     super.putAll(source);
    }

    private void addKeyToIndex(Object key) {

     if (!keyIndex.contains(key)) {
      keyIndex.add(key);
     }
    }

    @Override
    public Object remove(Object key) {

     keyIndex.remove(key);
     return super.remove(key);
    }
}

EDIT: I deliberately did not delve into the generics side of this...

Adriaan Koster