本文参考Thinkin in java 4版本。

Set与存储顺序

当你创建自己的类型时,要意识到Set需要一种方式来维护存储顺序,而存储顺序如何维护,则是在Set的不同实现之间会有所变化。因此,不同的Set实现不仅具有不同的行为,而且它们对于可以在特定的Set中放置的元素的类型也有不同的要求:

Set(interface) 存入Set的每个元素都必须是唯一的,因为Set不保存重复元素。加入Set的元素必须定义equals方法来已确保对象的唯一性。Set与Collection有完全一样的接口。Set接口不保证维护元素的次序。

HashSet* 为快速查找而设计的Set。存入HashSet元素必须定义hashCode方法。

TreeSet 保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。元素必须实现Comparable接口。

LinkedHashSet 具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序)。

上面HashSet被我打上了星号,如果没有其他限制,它就是你默认的选择,因为它对速度进行了优化。

本人对Thinking in java 罗里吧嗦的话进行了总结:

您只要用Set,tama哒别管别的,必须给我重写equals方法和hashCode方法。

请往下瞅—上代码辣:

Set案例:

下面的示例演示为了成功使用特定的Set实现类而必须定义的方法。

import java.util.*;

class SetType {
  int i;
  public SetType(int n) { i = n; }
  public boolean equals(Object o) {
    return o instanceof SetType && (i == ((SetType)o).i);
  }
  public String toString() { return Integer.toString(i); }
}

class HashType extends SetType {
  public HashType(int n) { super(n); }
  public int hashCode() { return i; }
}

class TreeType extends SetType
implements Comparable<TreeType> {
  public TreeType(int n) { super(n); }
  public int compareTo(TreeType arg) {
    return (arg.i < i ? -1 : (arg.i == i ? 0 : 1));
  }
}

public class TypesForSets {
  static <T> Set<T> fill(Set<T> set, Class<T> type) {
    try {
      for(int i = 0; i < 10; i++)
          set.add(
            type.getConstructor(int.class).newInstance(i));
    } catch(Exception e) {
      throw new RuntimeException(e);
    }
    return set;
  }
  static <T> void test(Set<T> set, Class<T> type) {
    fill(set, type);
    fill(set, type); // Try to add duplicates
    fill(set, type);
    System.out.println(set);
  }
  public static void main(String[] args) {
    test(new HashSet<HashType>(), HashType.class);
    test(new LinkedHashSet<HashType>(), HashType.class);
    test(new TreeSet<TreeType>(), TreeType.class);
    // Things that don't work:
    test(new HashSet<SetType>(), SetType.class);
    test(new HashSet<TreeType>(), TreeType.class);
    test(new LinkedHashSet<SetType>(), SetType.class);
    test(new LinkedHashSet<TreeType>(), TreeType.class);
    try {
      test(new TreeSet<SetType>(), SetType.class);
    } catch(Exception e) {
      System.out.println(e.getMessage());
    }
    try {
      test(new TreeSet<HashType>(), HashType.class);
    } catch(Exception e) {
      System.out.println(e.getMessage());
    }
  }
} /* Output: (Sample)
[2, 4, 9, 8, 6, 1, 3, 7, 5, 0]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
[9, 9, 7, 5, 1, 2, 6, 3, 0, 7, 2, 4, 4, 7, 9, 1, 3, 6, 2, 4, 3, 0, 5, 0, 8, 8, 8, 6, 5, 1]
[0, 5, 5, 6, 5, 0, 3, 1, 9, 8, 4, 2, 3, 9, 7, 3, 4, 4, 0, 7, 1, 9, 6, 2, 1, 8, 2, 8, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
java.lang.ClassCastException: SetType cannot be cast to java.lang.Comparable
java.lang.ClassCastException: HashType cannot be cast to java.lang.Comparable
*///:~

案例详解

为了证明哪些方法对于某种特定的Set是必须的,并且同时还要避免代码重复,我们创建了三个类。基类SetType只存储一个int,并且通过toString方法产生它的值。因为所有在Set中存储的类都必须有equals方法,因此在基类中也有该方法。其等价性是基于这个int类型的值确定。

HashType集成自SetType,并且添加了hashCode方法。该方法对于放置到Set散列实现中的对象来说是必须的。

TreeType实现了Comparable接口,如果一个对象被用于任何种类的排序容器中,例如
SortedSet(TreeSet是其实现类),那么它必须实现这个接口。

存储顺序(SortedSet):

SortedSet 中的元素可以保证处于排序状态,这使得它可以通过在SortedSet接口中下列方法提供附加的功能: SortedSet 中的comparator() 方法返回一个Comparator对象,或者返回null,表示已自然方式排序。

Object first(): 返回容器中的第一个元素。
Object last(): 返回容器中的最末一个元素。
SortedSet subSet(fromElement,toElement)生成此Set的子集 范围从 fromElement(包含),toElement(不包含)

SortedSet HedSet(toElement)生成此Set的子集由小于toElement的元素组成。
SortedSet HedSet(toElement)生成此Set的子集由小于toElement的元素组成。

SortedSet案例:

//: containers/SortedSetDemo.java
// What you can do with a TreeSet.
import java.util.*;


public class SortedSetDemo {
  public static void main(String[] args) {
    SortedSet<String> sortedSet = new TreeSet<String>();
    Collections.addAll(sortedSet,
      "one two three four five six seven eight"
        .split(" "));
    System.out.println(sortedSet);
    String low = sortedSet.first();
    String high = sortedSet.last();
    System.out.println(low);
    System.out.println(high);
    Iterator<String> it = sortedSet.iterator();
    for(int i = 0; i <= 6; i++) {
      if(i == 3) low = it.next();
      if(i == 6) high = it.next();
      else it.next();
    }
    System.out.println(low);
    System.out.println(high);
    System.out.println(sortedSet.subSet(low, high));
    System.out.println(sortedSet.headSet(high));
    System.out.println(sortedSet.tailSet(low));
  }
} /* Output:
[eight, five, four, one, seven, six, three, two]
eight
two
one
two
[one, seven, six, three]
[eight, five, four, one, seven, six, three]
[one, seven, six, three, two]
*///:~

*注意: SortedSet意思是“按对象的比较函数对元素排序 ”,而不是指“元素插入的次序”。
插入顺序可以用LinkedHashSet来保存。*