Java Set接口的API声明:
例如,某些实现禁止
null
元素,有些实现对元素类型有限制
我正在寻找一个不需要排序的基本Set实现(ArrayList提供List接口)并且不允许null
。 TreeSet,HashSet和LinkedHashSet都允许使用null元素。此外,TreeSet要求元素实现Comparable。
目前似乎没有这样的基本Set
。有谁知道为什么?或者,如果确实存在,我可以找到它?
[编辑]:我不想允许null
,因为稍后在代码中我的类将迭代集合中的所有元素并调用特定方法。 (我实际上正在使用HashSet<MyRandomObject
&gt;)。我宁愿失败而不是以后失败,或者由于null
在集合中而意外地招致一些奇怪的行为。
答案 0 :(得分:27)
比扩展特定实现更好,您可以轻松编写Set
的代理实现来检查null
。这与Collections.checkedSet
类似。除了适用于任何实现之外,您还可以确保已覆盖所有适用的方法。通过扩展具体集合找到了许多缺陷,然后在以后的版本中添加了其他方法。
答案 1 :(得分:22)
我会说使用组合而不是继承...它可能会更多工作,但面对Sun可能对集合框架所做的任何更改,它会更稳定。
public class NoNullSet<E> implements Set<E>
{
/** The set that is wrapped. */
final private Set<E> wrappedSet = new HashSet<E>();
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版本中保持为真)。
答案 2 :(得分:7)
没有基本的专有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);
答案 3 :(得分:2)
您可以轻松编写自己的类,通过继承适当的现有类,并覆盖所有相关方法,以便无法添加null
元素。
答案 4 :(得分:2)
您可以使用apache集合及其PredicatedCollection class,并将谓词设置为不允许空值。如果有人发送空值,您将获得例外。
答案 5 :(得分:2)
这是一种失败的通用方法 - 您提供了一个过滤器实现,可以限制以您想要的方式添加的内容。看一下java.util.Collections的源代码,了解有关包装的想法(我认为我对FilteredCollection类的实现是正确的......但它没有经过扩展测试)。最后有一个示例程序显示了用法。
public interface Filter<T>
{
boolean accept(T item);
}
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
public class FilteredCollections
{
private FilteredCollections()
{
}
public static <T> Collection<T> filteredCollection(final Collection<T> c,
final Filter<T> filter)
{
return (new FilteredCollection<T>(c, filter));
}
private static class FilteredCollection<E>
implements Collection<E>,
Serializable
{
private final Collection<E> wrapped;
private final Filter<E> filter;
FilteredCollection(final Collection<E> collection, final Filter<E> 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<E> iterator()
{
return new Iterator<E>()
{
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> 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<T>
implements Filter<T>
{
public boolean accept(final T item)
{
return (item != null);
}
}
public static void main(final String[] argv)
{
final Collection<String> strings;
strings = FilteredCollections.filteredCollection(new ArrayList<String>(),
new NullFilter<String>());
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);
}
}
答案 6 :(得分:2)
Yes - 在com.google.common.collect.ImmutableSet
的文档中:
具有可靠的,用户指定的迭代顺序的高性能,不可变集合。 不允许使用null元素。
答案 7 :(得分:1)
您可能还希望查看Google Collections。我相信他们更加无效。
答案 8 :(得分:1)
overrode the add function
Collection<String> errors = new HashSet<String>() {
@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)
}
};
答案 9 :(得分:0)
我不确定这是真的。但是,您是否可以从您选择的集合或HashTable继承并覆盖Add方法,如果该元素为null则抛出异常?
答案 10 :(得分:0)
顺便说一句,如果您要求Map
实现不允许空值,则旧java.util.Hashtable
不会。
答案 11 :(得分:0)
在这个特定的问题/例子中,如果你在开始迭代你提到的所有元素之前有一个HashSet<MyRandomObject> mySet
调用mySet.remove(null)
吗?
答案 12 :(得分:0)
[编辑]:我不想允许空值,因为稍后在代码中 类将遍历集合中的所有元素并调用 具体方法。
我们无需每次检查null
,只需在遍历集合之前简单地删除空的 。
您可以使用set.remove(null);
Set<String> set = new HashSet<>();
set.add("test");
set.add(null);
set.add(null);
System.out.println(set);
set.remove(null);
System.out.println(set);
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
输出
[null, test]
[test]
test
答案 13 :(得分:-1)
为什么你不想允许null
?
如果null
添加到您的设置中,您是否要抛出异常?如果是这样,请执行以下操作:
private Set<Object> mySet = new HashSet<Object>() {
@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);
}
};
HashSet
addAll()
重复调用add()
,因此这是您必须覆盖的唯一方法。
答案 14 :(得分:-2)
Hashtable不允许空值......