Good way to get *any* value from a Java Set?
Asked Answered
O

3

99

Given a simple Set<T>, what is a good way (fast, few lines of code) to get any value from the Set?

With a List, it's easy:

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

But, with a Set, there is no .get(...) method because Sets are not ordered.

Offal answered 3/12, 2012 at 22:13 Comment(0)
O
139

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.

Offal answered 3/12, 2012 at 22:13 Comment(4)
Actually, when you don't want a default, we want you to go ahead and use iterator().next(). (This is why we don't have a getFirst(Iterable<E>), just a getFirst(Iterable<E>, E default).)Grate
@LouisWasserman: Not necessarily. If the set was very large, and had most of its items removed, it will need to loop through until it find a non-empty bucket. https://mcmap.net/q/218519/-why-is-dictionary-first-so-slowFrond
True, but extremely rare in practice. (Also, easily addressed by using a LinkedHashSet.)Grate
(Also, I claim no faster solution exists.)Grate
C
23

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.

Catchment answered 23/5, 2018 at 11:52 Comment(3)
I wonder if this is faster or slower than set.iterator().next();.Echo
set.iterator().next() will throw a NoSuchElementException in case the collection is empty, using findAny of Stream is safer since it'll give you an empty Optional if the the collection is empty. I would err on safety unless performance is really important. Really depends on what you need.Loach
This solution is preferable for 2 reasons. (1) the forced null-checking with Optional, and (2) .stream().findAny() is much more self-documenting than .iterator().next(). I.e. it's much clearer that you're happy to get any element of the set, rather than just the "next" in the iterator.Aimee
U
4

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!

Unisexual answered 29/3, 2017 at 14:56 Comment(1)
This isn't an answer to the question, though it does elaborate on why the question makes sense.Flosser

© 2022 - 2024 — McMap. All rights reserved.