Существует ли базовая реализация Java Set, которая не допускает нулевые значения? - PullRequest
27 голосов
/ 26 февраля 2009

API для интерфейсов Java Set :

Например, некоторые реализации запрещают null элементы, а некоторые имеют ограничения по типам их элементов

Я ищу реализацию базового набора, которая не требует упорядочения (так как ArrayList обеспечивает интерфейс List ) и которая не разрешает null. TreeSet , HashSet и LinkedHashSet все разрешают нулевые элементы. Кроме того, TreeSet требует, чтобы элементы реализовывали Comparable .

Кажется, что в настоящее время не существует такого базового Set. Кто-нибудь знает почему? Или, если кто-то существует, где я могу его найти?

[Edit]: я не хочу разрешать null s, потому что позже в коде мой класс будет перебирать все элементы в коллекции и вызывать определенный метод. (Я на самом деле использую HashSet<MyRandomObject>). Я предпочел бы быстро потерпеть неудачу, чем потерпеть неудачу позже, или случайно понести какое-то странное поведение из-за присутствия null в наборе.

Ответы [ 14 ]

27 голосов
/ 26 февраля 2009

Лучше, чем расширять конкретную реализацию, вы можете легко написать прокси-реализацию Set, которая проверяет null s. Это аналог Collections.checkedSet. Помимо применимости к любой реализации, вы также можете быть уверены, что переопределили все применимые методы. Многие недостатки были обнаружены при расширении бетонных коллекций, в которые затем были добавлены дополнительные методы в более поздних версиях.

22 голосов
/ 26 февраля 2009

Я бы сказал, использовать композицию вместо наследования ... это может быть больше работы, но она будет более стабильной перед лицом любых изменений, которые 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).

7 голосов
/ 21 сентября 2015

Нет базовой проприетарной реализации Set, которая игнорирует или ограничивает null! Существует EnumSet , но он предназначен для хранения типов перечислений.

Однако создания собственной реализации можно избежать, если вы используете Guava или Commons Collections :

1. Решение для гуавы:

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

2. Коллекции общин:

Set noNulls = new HashSet();
CollectionUtils.addIgnoreNull(noNulls, object);
2 голосов
/ 19 октября 2012

Да - в документах для com.google.common.collect.ImmutableSet:

Высокопроизводительный неизменяемый набор с надежным, заданным пользователем порядком итераций. Не разрешает нулевые элементы.

2 голосов
/ 27 февраля 2009

Это ошибочный универсальный способ сделать это - вы предоставляете реализацию Filter, которая может ограничивать то, что добавляется любым способом, каким вы хотите. Взгляните на источник для 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);
    }
}
2 голосов
/ 27 февраля 2009

Вы можете использовать коллекции apache и его класс PredicatedCollection и установить предикат так, чтобы он не допускал пустых значений. Вы получите исключения, если кто-то отправит нули.

2 голосов
/ 26 февраля 2009

Вы можете легко написать свой собственный, создав подклассы соответствующего существующего класса и переопределив все соответствующие методы, чтобы вы не могли добавить null элементы.

1 голос
/ 04 декабря 2017

для меня, я не нашел, поэтому я 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)
    }
};
1 голос
/ 26 февраля 2009

Вы также можете проверить Коллекции Google . Я считаю, что они более ноль фобические.

0 голосов
/ 13 февраля 2015

В этом конкретном вопросе / примере наверняка, если у вас есть HashSet<MyRandomObject> mySet вызов mySet.remove(null) перед началом итерации по всем элементам, которые вы упомянули?

...