从Java集获取* any *值的好方法?


71

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

使用List,很容易:

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

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

Answers:


101

ASet<T>是一个Iterable<T>,因此迭代到第一个元素即可:

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

番石榴有一种方法可以做到这一点,尽管上面的代码片段可能更好


1
实际上,当您不想使用默认值时,我们希望您继续使用iterator().next()。(这就是为什么我们没有一个getFirst(Iterable<E>),只是一个getFirst(Iterable<E>, E default)。)
Louis Wasserman

2
@GanGnaMStYleOverFlowErroR:是的。blog.stackoverflow.com/2011/07/...
SLaks

1
...什么?!不,不是。对于Set我所知道的任何实现,这最多是O(log n)。
Louis Wasserman

1
是的,但在实践中极为罕见。(也可以使用LinkedHashSet。轻松解决。)
Louis Wasserman

2
(此外,我声称不存在更快的解决方案。)
Louis Wasserman

12

由于存在流,因此也可以这样做,但是必须使用class java.util.OptionalOptional是元素或显式无元素(避免使用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();
}

编辑:我Optional经常使用自己:有一种方法可以访问元素或获取默认值(如果不存在):
optT.orElse(other)返回元素,或者如果不存在,则返回otherother可能是null,顺便说一句。


1
我想知道这是比快还是慢set.iterator().next();
古斯塔沃'18

set.iterator()。next()将在集合为空的情况下抛出NoSuchElementException,使用Stream的findAny更安全,因为如果集合为空,它将为您提供空的Optional。除非性能真的很重要,否则我会在安全方面犯错。真的取决于您的需求。
lost_trekkie

3

从集合或集合中获取任何元素似乎是一种不常见的需求-即使不是任意的或折衷的-但是,例如当人们需要计算Map中的键或值对象的统计信息并且必须初始化min /最大值。在更新每个元素的最小/最大值之前,将使用Set / Collection中的any元素(由Map.keySet()或Map.values()返回)进行初始化。

那么,面对这个问题并试图保持较小的内存和执行时间以及清晰的代码时,人们有什么选择呢?

通常,您会得到通常的结果:“将Set转换为ArrayList并获取第一个元素”。大!另一个数百万个项目的数组和额外的处理周期,用于从Set中检索对象,分配数组并进行填充

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() ){ ... }
 ...
}

或者可以使用带有迭代器的循环,使用标志来表示需要初始化min / max,并使用条件语句来检查是否为循环中的所有迭代设置了该标志。这意味着很多条件检查。

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

或者在循环外进行初始化:

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) );
}

但是,每当需要最小/最大时,代表程序员(以及代表JVM设置Iterator)的所有这些操作真的值得吗?

来自javally正确的运动的建议很可能是:“将您的Map包裹在一个类中,该类在放置或删除时跟踪最小值和最大值!”。

根据我的经验,还有另一种情况,即仅需要地图中的任何物品。这是当地图包含具有共同属性的对象时-该映射中的所有对象都相同-并且您需要读取该属性。例如,假设有一个具有相同直方图的容纳箱的图,它们具有相同的维数。给定这样一个Map,您可能需要知道Map中任何Histobin的维数,以便创建另一个具有相同维数的Histobin。我是否需要再次设置迭代器,并在调用next()一次后将其处置?对于这种情况,我将跳过javally正确人士的建议。

而且,如果在获取任何元素时遇到的所有麻烦导致内存不足,并且CPU周期增加,那么为了获得难以获得的任何元素而必须编写的所有代码呢?

我们需要任何要素。把它给我们!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.