是否存在不允许空值的基本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 extends E> 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 extends E> 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 extends E> 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.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
HashSet
的addAll()
重复调用add()
,因此这是您必须覆盖的唯一方法。
Hashtable不允许空值……