Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if only one element exist using Guava

Recently I had a need to do 'special case' scenario if only one element exist in collection. Checking for ...size() == 1 and retrieving by using ...iterator.next() looked ugly so I've created two methods in home brew Collections class:

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

Few days ago I discovered that Guava has method called Iterables.getOnlyElement. It covers my need and replaces singleValue, but I can't find match for isSingleValue. Is that by design? Is it worth to put feature request for having Iterables.isOnlyElement method?

EDIT: Since there were few upvotes I decided to open enhancement on guava - issue 957. Final resolution - 'WontFix'. Arguments are very similar to what Thomas/Xaerxess provided.

like image 260
Petro Semeniuk Avatar asked Apr 03 '12 06:04

Petro Semeniuk


2 Answers

Well, you'd not gain much by replacing values.size() == 1 with a method, except you could check for null. However, there are methods in Apache Commons Collections (as well as in Guava, I assume) to do that.

I'd rather write if( values.size() == 1 ) or if( SomeHelper.size(values) == 1 )
than if( SomeHelper.isSingleValue(values) ) - the intent is much clearer in the first two approaches and it's as much code to write as with the third approach.

like image 121
Thomas Avatar answered Sep 20 '22 09:09

Thomas


Just in addition to other answers (I was going to write something like @daveb who deleted his one: If there is not exactly one element, then Iterables#getOnlyElement will throw an IllegalArgumentException or NoSuchElementException) - an answer to question why there isn't any Iterables.isSingleValue(Iterable) in Guava.

I think you're making this wrong. If:

  • method invokation doesn't change state (unlike next() in iterator, that's why hasNext() exists)
  • and you can clearly and exlicitly say that value returned isn't exceptional case (unlike null returned from Map#get(Object) - it can be null value or it can mean that key wasn't found in map)

there is no need for method checking if condition is true and then doing some operation (with assertion in it!) like in your sample code.

If you are absolutely sure that the iterable at this place cannot have size other than 1, than condition checking is redundant (exception is thrown in other cases).
If you want only get first element in non-empty collection - collection.iterator.next() is perfectly OK (NoSuchElementException is thrown if collection is empty).
If you don't know anything about collection's size than Iterables.getFirst(iterable, default) is for you.

P.S. If your Collections#isSingleValue used only locally here (hence could be private) that really means you don't need that check before calling Iterables#getOnlyValue.

P.P.S. Another answer to your question about Guava's design could be Item 57 of Joshua Bloch's Effective Java - there are few different helper methods in Guava I mentioned before that explicitly say what is an exceptional case for each one; boolean check wasn't added becasuse of keeping API as small as possible.

like image 33
Xaerxess Avatar answered Sep 20 '22 09:09

Xaerxess