Is there a basic Java Set implementation that does

2020-05-31 06:25发布

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 nulls, 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.

标签: java api null set
15条回答
Viruses.
2楼-- · 2020-05-31 07:04

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);
    }
}
查看更多
The star\"
3楼-- · 2020-05-31 07:09

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.

查看更多
成全新的幸福
4楼-- · 2020-05-31 07:09

BTW, if you'd asked for a Map implementation that does not allow nulls, the old java.util.Hashtable does not.

查看更多
甜甜的少女心
5楼-- · 2020-05-31 07:13

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?

查看更多
三岁会撩人
6楼-- · 2020-05-31 07:19

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);
查看更多
你好瞎i
7楼-- · 2020-05-31 07:19

[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
查看更多
登录 后发表回答