33

Java Set接口的 API 指出:

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

我正在寻找一个不需要排序的基本 Set 实现(因为ArrayListList接口提供)并且不允许null. TreeSetHashSetLinkedHashSet都允许空元素。此外, TreeSet 要求元素实现Comparable

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

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

4

15 回答 15

28

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

于 2009-02-26T16:06:23.973 回答
22

我会说使用组合而不是继承......它可能会做更多的工作,但面对 Sun 可能对 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 ...
}

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

于 2009-02-26T16:28:30.007 回答
13

没有忽略或约束 null 的基本专有 Set 实现!有EnumSet,但它是为包含枚举类型量身定制的。

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

1.番石榴解决方案:

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

2.公共收藏:

Set noNulls = new HashSet();
CollectionUtils.addIgnoreNull(noNulls, object);
于 2015-09-21T14:57:21.443 回答
2

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

于 2009-02-27T03:57:28.570 回答
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);
    }
}
于 2009-02-27T06:08:47.460 回答
2

的 - 在文档中com.google.common.collect.ImmutableSet

具有可靠的、用户指定的迭代顺序的高性能、不可变 Set。不允许空元素。

于 2012-10-19T17:48:56.597 回答
1

您可以通过继承适当的现有类并覆盖所有相关方法来轻松编写自己的方法,这样您就无法添加null元素。

于 2009-02-26T15:54:47.997 回答
1

您可能还希望查看Google Collections。我相信他们更害怕零。

于 2009-02-26T16:50:16.083 回答
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)
    }
};
于 2017-12-04T17:41:34.137 回答
1

[编辑]:我不想允许空值,因为稍后在代码中我的类将遍历集合中的所有元素并调用特定方法。

我们可以在迭代 set 之前简单地删除一次nullnull ,而不是检查。

您可以使用删除空值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
于 2020-02-27T14:11:19.843 回答
0

顺便说一句,如果您要求一个Map不允许空值的实现,旧的java.util.Hashtable则不允许。

于 2009-06-29T07:03:52.467 回答
0

在这个特定的问题/示例中,如果您 在开始迭代您提到的所有元素之前 有一个HashSet<MyRandomObject> mySet电话?mySet.remove(null)

于 2015-02-12T21:43:07.933 回答
-1

我不确定这是否属实。但是您能否不从您选择的集合或 HashTable 继承并覆盖 Add 方法,如果元素为空则抛出异常?

于 2009-02-26T15:55:13.347 回答
-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's反复addAll()调用add(),因此这是您必须覆盖的唯一方法。

于 2009-02-26T15:57:15.137 回答
-2

Hashtable 不允许空值......

于 2012-08-23T05:26:10.360 回答