2009-02-26 2 views
25

API-интерфейс для интерфейсных состояний Java Set:Есть ли базовая реализация Java Set, которая не допускает null?

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

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

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

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

+0

Все наборы в значительной степени должны реализовать какой-то способ быстро найти дубликаты, либо Сопоставимые или хеш-коды, в противном случае сканирование для дубликатов было бы слишком болезненным. –

+0

Вы можете дать TreeSet Comparator, что означает, что элементы не обязательно должны быть сравнимыми (должен был быть отдельный метод создания для режима Comparable, IMO). –

+0

Вы также можете проверить, нет ли чего-то, когда вы вытаскиваете его из набора. – cdmckay

ответ

27

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

+1

Любая идея, почему Sun этого не делала? –

+0

Солнце? Обновления в структуре коллекции, скорее всего, будут зависеть от Google. Кстати, надо надеяться, что коллекции BOF - JavaOne. –

+0

классический пример предпочтительной композиции над наследованием .. + 1 – Inquisitive

2

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

+0

Не забывайте allAll и конструкторы! –

+0

На самом деле, addAll и конструкторы не нужно переопределять, поскольку они определены в AbstractSet и AbstractCollection, чтобы просто вызвать метод add. Так что только добавление действительно нужно переопределить. –

+0

Возможно, вам лучше использовать композицию вместо подклассификации, так как вы не контролируете класс, который вы подклассифицируете (что, если Sun добавит новый метод к наборам, который позволит пользователям добавлять null?) – cdmckay

0

Я не уверен, что это правда. Но не могли бы вы наследовать из коллекции или HashTable по вашему выбору и переопределить метод Add, выбрасывая исключение, если элемент имеет значение null?

-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() неоднократно, так что это единственный метод, вы должны переопределить.

+0

Вы не должны рассчитывать на addAll(), вызывающий add(), поскольку это деталь реализации и может не всегда быть правдой. – cdmckay

+0

@cdmckay: Если вы еще не подтвердили ответ Тома Хотина, сделайте это сейчас! :) –

+0

О, я вижу, вы вместо этого добавили свой собственный ответ. –

21

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

Edit: Также обратите внимание, что эта реализация не зависит от addAll вызова добавить (который является деталью реализации и не должен использоваться, поскольку она не может быть гарантирована оставаться верными во всех Java-релизах).

Редактировать: Добавлено более подробное сообщение об ошибке.

Редактировать: Сделано так ясно() ничего не возвращает.

Редактировать: Сделано так, чтобы оно было добавлено (E e).

Редактировать: Throw IllegalArgumentException вместо NullPointerException.

+2

вместо того, чтобы бросать NullPointerException, подумайте о том, чтобы бросить RuntimeException («e == null») (или исключение IllegalArgumentException), поскольку это говорит разработчику, читающему стек стек, что это преднамеренно заброшенное исключение, а не логическая ошибка. –

+1

Или добавить описательное сообщение к исключению NullPointerException. Кроме того, +1 для комплектования над наследованием. –

+0

throw IllegalArgumentException, поскольку это то, за что это необходимо, незаконный аргумент (null) –

1

Возможно, вы также захотите зарегистрироваться Google Collections. Думаю, они более пустые фобии.

2

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

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); 
    } 
} 
0

Кстати, если бы вы попросили Map реализации, которая не позволяет аннулирует, старый java.util.Hashtable не делает.

-2

Hashtable не допускает пустых значений ......

+1

'Hashtable' не является' Set' –

2

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

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

+0

. Эта реализация также имеет недостаток в неизменности. Из документов: «По этой причине, и чтобы избежать общей путаницы, настоятельно рекомендуется помещать в эту коллекцию только неизменяемые объекты». Не думайте, что это просил ОП. –

0

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

4

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

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

1. Guava Решение:

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

2.Commons Коллекции:

Set noNulls = new HashSet(); 
CollectionUtils.addIgnoreNull(noNulls, object); 
0

для меня, я не нашел один, , так что я 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) 
    } 
};