Writing contains() for a generic collection

后端 未结 4 1362
北海茫月
北海茫月 2020-12-18 10:42

I\'m writing a skiplist class in java as an excercise. I\'ve written a class called SkipListInternal which contains the actual skiplist. I\'ve also mad

相关标签:
4条回答
  • 2020-12-18 10:57

    Since the contains() is from java.util.Collection, We are supposed to follow the Collection.contains() contract. Because throwing ClassCastException is an optional behavior, it's correct to return false in your code when cast fails. So I think your implementation comply with the contract.

            /**
             * Returns true if this collection contains the specified element.
             * More formally, returns true if and only if this collection
             * contains at least one element e such that
             * (o==null ? e==null : o.equals(e)).
             *
             * @param o element whose presence in this collection is to be tested
             * @return <tt>true</tt> if this collection contains the specified
             *         element
             * @throws ClassCastException if the type of the specified element
             *         is incompatible with this collection (optional)
             * @throws NullPointerException if the specified element is null and this
             *         collection does not permit null elements (optional)
             */
             boolean contains(Object o);
    
    0 讨论(0)
  • 2020-12-18 11:05

    In order for a sorted set implementation to work, the elements of the set have to have an ordering. This may either be "natural" (i.e., the elements implement Comparable) or "imposed" (by using an explicit Comparator during set construction).

    So, the first thing is, that you'd probably rather use the ordering defined for the set elements (after all, you are implementing a SortedSet!) instead of equals in contains for efficiency. I assume, that you are already using an ordering in your internal SkipListInternal<E> -- how'd you maintain the Sorted in SortedSet given only equals?

    The fact that contains is actually declared as contains(Object key) in the interface is really unfortunate. I'd do, what the TreeMap implementation does (which is the underlying container for TreeSet, the standard SortedSet from the collections framework):

    if (comparator != null)
        return getEntryUsingComparator(key);
    if (key == null)
        throw new NullPointerException();
    Comparable<? super K> k = (Comparable<? super K>) key;
    

    i.e., cast, assuming the client application using your collection behaves sanely.

    0 讨论(0)
  • 2020-12-18 11:09

    Strictly speaking such an implementation would be wrong.

    The reason for this is that even if an object is not of type E, it could still return true on an equals() call.

    Assume for a second, that you've got a class like this:

    public class FakeString {
      private final String value;
    
      public FakeString(String value) {
        if (value == null) {
          throw new IllegalArgumentException();
        }
        this.value = value;
      }
    
      public int hashCode() {
        return value.hashCode();
      }
    
      public boolean equals(Object o) {
        return value.equals(o);
      }
    }
    

    Then this code would print true:

    List<String> strings = Arrays.asList("foo", "bar", "baz");
    System.out.println(strings.contains(new FakeString("bar")));
    

    And just to clarify: this behaviour is intended and is the reason why contains() takes an Object instead of E. The same is true for remove(), by the way.

    0 讨论(0)
  • 2020-12-18 11:21

    @Joaschim comment is correct for standard collections, however if you want a checked collection I suggest you check what can be added, and not optimise for the lookups of invalid types (unless you want to throw an Exception) You can do something like.

    public class SkipListSet<E> implements SortedSet<E>{
       private final Class<E> eClass;
       private final SkipListInternal<E> skiplist;
    
       ...
       public boolean add(Object o) {
           if(!eClass.isInstanceOf(o))
        // OR
           if(eClass != o.getClass())
               throw new IllegalArgumentException("Type "+o.getClass()+" is not a "+eClass);
           skiplist.add(o);
       }
    
       public boolean contains(Object o){
           // if (!(o instanceof E)) return false; // don't need to optmise for this.
           return skiplist.find((E) o) != null;
       }
    
       ...
    
    }
    

    BTW: Java has a builtin thread safe ConcurrentSkipListSet and ConcurrentSkipListMap You might find it interesting to read the source for this. ;)

    0 讨论(0)
提交回复
热议问题