使用Guava检查是否只存在一个元素

时间:2012-04-03 06:29:14

标签: java collections guava

如果集合中只存在一个元素,我最近需要做'特殊情况'场景。检查...size() == 1并使用...iterator.next()检索看起来很难看,所以我在home brew Collections类中创建了两种方法:

public class Collections {
    public static <T> boolean isSingleValue(Collection<T> values) {
        return values.size() == 1;
    }

    public static <T> T singleValue(Collection<T> values) {
        Assert.isTrue(isSingleValue(values));
        return values.iterator().next();
    }
}

前几天我发现Guava有一种名为Iterables.getOnlyElement的方法。它涵盖了我的需求并替换了singleValue,但找不到isSingleValue的匹配项。这是设计的吗?是否值得为Iterables.isOnlyElement方法提出功能请求?

修改 由于赞成很少,我决定在番石榴上开放增强 - issue 957。最终决议 - 'WontFix'。争论与Thomas / Xaerxess提供的非常相似。

3 个答案:

答案 0 :(得分:10)

嗯,用一个方法替换values.size() == 1你不会获得太多收益,除非你可以检查null。但是,Apache Commons Collections(以及我假设的Guava)中有一些方法可以做到这一点。

我宁愿写if( values.size() == 1 )if( SomeHelper.size(values) == 1 )而不是if( SomeHelper.isSingleValue(values) ) - 前两种方法的意图更清晰,而且编写的代码与第三种方法。

答案 1 :(得分:6)

除了其他答案之外(我打算写一些类似@daveb的人删除他的那个:如果没有一个元素,那么Iterables#getOnlyElement会抛出一个IllegalArgumentException或者NoSuchElementException ) - 回答问题,为什么番石榴中没有Iterables.isSingleValue(Iterable)

我认为你犯了这个错误。如果:

  • 方法invokation不会改变状态(与迭代器中的next()不同,这就是hasNext()存在的原因)
  • 并且您可以清楚明确地说返回的值不是例外情况(与null返回的Map#get(Object)不同 - 它可以是空值,也可能意味着在地图中找不到该键)

如果条件为真,则不需要方法检查,然后在示例代码中执行某些操作(使用断言!)。

如果你完全确定这个地方的迭代不能有1以外的大小,那么条件检查是多余的(在其他情况下抛出异常)。
如果你只想获得非空集合中的第一个元素 - collection.iterator.next()完全没问题(如果集合为空则抛出NoSuchElementException。)
如果您对集合的大小一无所知,那么Iterables.getFirst(iterable, default)就是适合您。

P.S。如果您的Collections#isSingleValue仅在本地使用(因此可能是私有的)真的意味着您在调用Iterables#getOnlyValue之前不需要该检查。

P.P.S。关于Guava设计的问题的另一个答案可能是Joshua Bloch的 Effective Java 的第57项 - 我之前提到过的Guava中几乎没有不同的辅助方法明确说明每个方法的例外情况;因为保持API尽可能小,所以没有添加布尔检查。

答案 2 :(得分:0)

现在我遇到了同样的问题。

我将用这段代码解决:

public static <T> void hasJustOne(T... values) {
    hasJustOne(Predicates.notNull(), values);
}

public static <T> void hasJustOne(Predicate<T> predicate, T... values) {
    Collection<T> filtred = Collections2.filter(Arrays.asList(values),predicate);
    Preconditions.checkArgument(filtred.size() == 1);
}