是否存在不允许空值的基本Java Set实现?

Java Set接口的API指出:

例如,某些实现禁止null元素,而某些实现对其元素的类型有限制

我正在寻找一个不需要排序的基本Set实现(如ArrayList为List接口提供的)并且不允许null 。 TreeSet , HashSet和LinkedHashSet都允许null元素。 另外,TreeSet要求元素实现Comparable 。

似乎目前没有这样的基本Set 。 有谁知道为什么? 或者,如果确实存在,我可以找到它?

[编辑]:我不想允许null ,因为稍后在代码中我的类将迭代集合中的所有元素并调用特定方法。 (我实际上正在使用HashSet<MyRandomObject >)。 我宁愿快速失败而不是失败,或者由于集合中的null而意外地产生一些奇怪的行为。

比扩展特定实现更好,您可以轻松编写Set的代理实现来检查null 。 这类似于Collections.checkedSet 。 除了适用于任何实现之外,您还可以确保已覆盖所有适用的方法。 通过扩展具体集合找到了许多缺陷,然后在以后的版本中添加了其他方法。

我会说使用组合而不是inheritance…它可能更多的工作,但面对Sun可能对集合框架所做的任何更改,它会更稳定。

 public class NoNullSet implements Set { /** The set that is wrapped. */ final private Set wrappedSet = new HashSet(); public boolean add(E e) { if (e == null) throw new IllegalArgumentException("You cannot add null to a NoNullSet"); return wrappedSet.add(e); } public boolean addAll(Collection c) { for (E e : c) add(e); } public void clear() { wrappedSet.clear(); } public boolean contains(Object o) { return wrappedSet.contains(o); } ... wrap the rest of them ... } 

编辑:另请注意,此实现不依赖于addAll调用add(这是一个实现细节,不应该使用,因为它不能保证在所有Java版本中都保持为true)。

编辑:添加了更多描述性错误消息。

编辑:使它如此清晰()不返回任何东西。

编辑:这样就可以添加(E e)。

编辑:抛出IllegalArgumentException而不是NullPointerException。

没有基本的专有Set实现忽略或约束null! 有EnumSet ,但是那个是用于包含枚举类型的裁缝。

但是,如果您使用Guava或Commons Collections ,则可以避免创建自己的实现:

1.番石榴溶液:

 Set noNulls = Constraints.constrainedSet(new HashSet(), Constraints.notNull()); 

2. Commons Collections:

 Set noNulls = new HashSet(); CollectionUtils.addIgnoreNull(noNulls, object); 

您可以通过inheritance适当的现有类来轻松编写自己的类,并覆盖所有相关方法,以便您不能添加null元素。

您可以使用apache集合及其PredicatedCollection类 ,并将谓词设置为不允许空值。 如果有人发送空值,您将获得例外。

这是一种失败的通用方法 – 您提供了一个Filter实现,可以限制以您想要的方式添加的内容。 看一下java.util.Collections的源代码,了解有关包装的想法(我认为我对FilteredCollection类的实现是正确的……但它没有经过扩展测试)。 最后有一个示例程序显示用法。

 public interface Filter { boolean accept(T item); } import java.io.Serializable; import java.util.Collection; import java.util.Iterator; public class FilteredCollections { private FilteredCollections() { } public static  Collection filteredCollection(final Collection c, final Filter filter) { return (new FilteredCollection(c, filter)); } private static class FilteredCollection implements Collection, Serializable { private final Collection wrapped; private final Filter filter; FilteredCollection(final Collection collection, final Filter f) { if(collection == null) { throw new IllegalArgumentException("collection cannot be null"); } if(f == null) { throw new IllegalArgumentException("f cannot be null"); } wrapped = collection; filter = f; } public int size() { return (wrapped.size()); } public boolean isEmpty() { return (wrapped.isEmpty()); } public boolean contains(final Object o) { return (wrapped.contains(o)); } public Iterator iterator() { return new Iterator() { final Iterator i = wrapped.iterator(); public boolean hasNext() { return (i.hasNext()); } public E next() { return (i.next()); } public void remove() { i.remove(); } }; } public Object[] toArray() { return (wrapped.toArray()); } public  T[] toArray(final T[] a) { return (wrapped.toArray(a)); } public boolean add(final E e) { final boolean ret; if(filter.accept(e)) { ret = wrapped.add(e); } else { // you could throw an exception instead if you want - // IllegalArgumentException is what I would suggest ret = false; } return (ret); } public boolean remove(final Object o) { return (wrapped.remove(o)); } public boolean containsAll(final Collection c) { return (wrapped.containsAll(c)); } public boolean addAll(final Collection c) { final E[] a; boolean result; a = (E[])wrapped.toArray(); result = false; for(final E e : a) { result |= wrapped.add(e); } return result; } public boolean removeAll(final Collection c) { return (wrapped.removeAll(c)); } public boolean retainAll(final Collection c) { return (wrapped.retainAll(c)); } public void clear() { wrapped.clear(); } public String toString() { return (wrapped.toString()); } } } import java.util.ArrayList; import java.util.Collection; public class Main { private static class NullFilter implements Filter { public boolean accept(final T item) { return (item != null); } } public static void main(final String[] argv) { final Collection strings; strings = FilteredCollections.filteredCollection(new ArrayList(), new NullFilter()); strings.add("hello"); strings.add(null); strings.add("world"); if(strings.size() != 2) { System.err.println("ERROR: strings.size() == " + strings.size()); } System.out.println(strings); } } 

是的 – 在com.google.common.collect.ImmutableSet的文档中:

具有可靠的,用户指定的迭代顺序的高性能,不可变集合。 不允许null元素。

您可能还希望查看Google Collections 。 我相信他们更加无效。

我不确定这是真的。 但是,您是否可以从您选择的集合或HashTableinheritance并覆盖Add方法,如果元素为null则抛出exception?

顺便说一句,如果你要求一个不允许空值的Map实现,那么旧的java.util.Hashtable就不会。

在这个特定的问题/例子中,如果你有一个HashSet mySet在你提到的所有元素开始迭代之前调用mySet.remove(null)吗?

对我来说,我没有找到一个,所以我overrode the add function

 Collection errors = new HashSet() { @Override public boolean add(String s) { return StringUtil.hasContent(s) && super.add(s);//we don't want add null and we allow HashSet.add(null) } }; 

为什么你不想允许null

如果将null添加到集合中,是否要抛出exception? 如果是这样,请执行以下操作:

 private Set mySet = new HashSet() { @Override public boolean add(Object e) { if (e == null) throw new IllegalArgumentException("null"); // or NPE // or, of course, you could just return false return super.add(e); } }; 

HashSetaddAll()重复调用add() ,因此这是您必须覆盖的唯一方法。

Hashtable不允许空值……