可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
The API for the Java Set interface states:
For example, some implementations prohibit null
elements and some have restrictions on the types of their elements
I am looking for a basic Set implementation that does not require ordering (as ArrayList provides for the List interface) and that does not permit null
. TreeSet, HashSet, and LinkedHashSet all allow null elements. Additionally, TreeSet has the requirement that elements implement Comparable.
It seems that no such basic Set
exists currently. Does anyone know why? Or if one does exist where I can find it?
[Edit]: I do not want to allow null
s, because later in the code my class will iterate over all elements in the collection and call a specific method. (I'm actually using HashSet<MyRandomObject
>). I would rather fail fast than fail later or accidentally incur some bizarre behavior due to a null
being in the set.
回答1:
Better than extending a particular implementation, you can easily write a proxy implementation of Set
that checks for null
s. This analogous to Collections.checkedSet
. Other than being applicable to any implementation, you can also be sure that you have overridden all applicable methods. Many flaws have been found by extending concrete collections which then have additional methods added in later versions.
回答2:
I would say use composition instead of inheritance... it might be more work but it'll be more stable in the face of any changes that Sun might make to the Collections Framework.
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 ...
}
Note that this implementation does not depend on addAll
calling add
(which is an implementation detail and should not be used because it cannot be guaranteed to remain true in all Java releases).
回答3:
There is no basic proprietary Set implementation that ignores or constrains null! There is EnumSet, but that one is tailors for the containment of enum types.
However, creating your own implementation can be avoided, if you use either Guava or Commons Collections:
1. Guava Solution:
Set noNulls = Constraints.constrainedSet(new HashSet(), Constraints.notNull());
2. Commons Collections:
Set noNulls = new HashSet();
CollectionUtils.addIgnoreNull(noNulls, object);
回答4:
You could use apache collections and its PredicatedCollection class, and set the predicate to not allow nulls. You will get exceptions if someone sends nulls in.
回答5:
This is a failry general purpose way of doing it - you provide a Filter implementation that can restrict what gets added in whatevber way you want. Take a look at the source for java.util.Collections for ideas on the wrapping (I think my implementaiton of the FilteredCollection class is correct... but it is not extensivly tested). There is a sample program at the end that shows the usage.
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:
Yes -- in the docs for com.google.common.collect.ImmutableSet
:
A high-performance, immutable Set with reliable, user-specified iteration order. Does not permit null elements.
回答7:
You could easily write your own, by subclassing an appropriate existing class, and overriding all relevant methods so that you can't add null
elements.
回答8:
You may also wish to check out Google Collections. They are more null phobic, I believe.
回答9:
for me, I didn't find one,
so I 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)
}
};
回答10:
BTW, if you'd asked for a Map
implementation that does not allow nulls, the old java.util.Hashtable
does not.
回答11:
In this particular question/example surely if you have a HashSet<MyRandomObject> mySet
call mySet.remove(null)
before starting the iteration over all elements you mentioned?
回答12:
[Edit]: I do not want to allow nulls, because later in the code my
class will iterate over all elements in the collection and call a
specific method.
Instead of checking null
, every time, we can simply remove the null once before iterating over the set.
You can remove the null values using 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());
}
Output
[null, test]
[test]
test
回答13:
I am not sure of a type which this is true. But could you not inherit from a collection or HashTable of your choice and override the Add method, throwing an exception if the element is null?
回答14:
Why do you not want to allow null
?
Do you want to throw an exception if null
is added to your set? If so, just do something like this:
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
's addAll()
calls add()
repeatedly, so this is the only method you'd have to override.
回答15:
Hashtable does not allow null values......