从 Java 集中获得任何值的好方法?

给定一个简单的 Set<T>,什么是从 Set中获得 任何值的好方法(快速的、几行代码) ?

使用 List很简单:

List<T> things = ...;
return things.get(0);

但是,对于 Set,没有 .get(...)方法,因为 Set不是有序的。

72222 次浏览

A Set<T> is an Iterable<T>, so iterating to the first element works:

Set<T> things = ...;
return things.iterator().next();

Guava has a method to do this, though the above snippet is likely better.

Getting any element from a Set or Collection may seem like an uncommon demand - if not arbitrary or eclectic - but, it is quite common when one, for example, needs to calculate statistics on Keys or Values objects in a Map and must initialise min/max values. The any element from a Set/Collection (returned by Map.keySet() or Map.values()) will be used for this initialisation prior to updating min/max values over each element.

So, what options one has when faced with this problem and at the same time trying to keep memory and execution time small and code clear?

Often you get the usual: "convert Set to ArrayList and get the first element". Great! Another array of millions of items and extra processing cycles to retrieve objects from Set, allocate array and populate it:

HashMap<K,V> map;
List<K> list = new ArrayList<V>(map.keySet()); // min/max of keys
min = max = list.get(0).some_property(); // initialisation step
for(i=list.size();i-->1;){
if( min > list.get(i).some_property() ){ ... }
...
}

Or one may use looping with an Iterator, using a flag to denote that min/max need to be initialised and a conditional statement to check if that flag is set for all the iterations in the loop. This implies a lot of conditional checking.

boolean flag = true;
Iterator it = map.keySet().iterator();
while( it.hasNext() ){
if( flag ){
// initialisation step
min = max = it.next().some_property();
flag = false;
} else {
if( min > list.get(i).some_property() ){ min = list.get(i).some_property() }
...
}
}

Or do the initialisation outside the loop:

HashMap<K,V> map;
Iterator it = map.keySet().iterator();
K akey;
if( it.hasNext() ){
// initialisation step:
akey = it.next();
min = max = akey.value();
do {
if( min > list.get(i).some_property() ){ min = akey.some_property() }
} while( it.hasNext() && ((akey=it.next())!=null) );
}

But is it really worth all this manouvre on the behalf of the programmer (and setting up the Iterator on behalf of the JVM) whenever min/max's needed?

The suggestion from a javally-correct ol' sport could well be: "wrap your Map in a class which keeps track of min and max values when put or deleted!".

There is another situation which in my experience the need for just any item from a Map arises. This is when the map contains objects which have a common property - all the same for all of them in that map - and you need to read that property. For example suppose there is a Map of holding bins of the same histogram which have the same number of dimensions. Given such a Map you may need to know the number of dimensions of just any Histobin in the Map in order to, say, create another Histobin of the same dimensions. Do I need to setup an iterator again and dispose it after calling next() just once? I will skip the javally-correct person's suggestion to this situation.

And if all the trouble in getting the any element causes insignificant memory and cpu cycles increase, then what about all the code one has to write just to get the hard-to-get any element.

We need the any element. Give it to us!

Since streams are present, you can do it that way, too, but you have to use the class java.util.Optional. Optional is a wrapper-class for an element or explicitely no-element (avoiding the Nullpointer).

//returns an Optional.
Optional <T> optT = set.stream().findAny();


//Optional.isPresent() yields false, if set was empty, avoiding NullpointerException
if(optT.isPresent()){
//Optional.get() returns the actual element
return optT.get();
}

Edit: As I use Optional quite often myself: There is a way for accessing the element or getting a default, in case it's not present:
optT.orElse(other) returns either the element or, if not present, other. other may be null, btw.