常见的:
我们在Set中很方便的使用了诸如Integer和String这样的Java预定义类型,这些类型被设计为可以在容器内部使用。
当你创建自己的类型时,要意识到Set需要一种方式来维护存储顺序,而存储顺序如何维护,则是在Set的不同实现之间会有所变化。因此,不同的Set实现不仅具有不同的行为,而且它们对于可以在特定的Set中放置的元素的类型也有不同的要求:
* ---------------------------------------------------------------------------------------- * Set(interface) 存入Set的每个元素必须是唯一的,因为Set不保存重复元素。加入Set的元素必须定义equals()方法以确保对象的唯一性。Set与Collection有完全一样的接口。Set接口不保证维护元素的次序。 * HashSet(*) 为快速查找而设计的Set。存入HashSet的元素必须定义hashCode() * TreeSet 保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。元素必须实现Comparable接口。 * LinkedHashSet 具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序)。于是在使用迭代器遍历Set时,结果会按照插入的次序显示。元素也必须定义hashCode()方法。在HashSet上打上星号表示,如果没有其他的限制,这就应该是你的默认的选择,因为它对速度进行了优化。
你必须为散列存储和树状存储都创建一个equals()方法,但是hashCode()只有在这个类将会被置于HashSet或者LinkedHashSet中才是必须的。但是,对于良好的编程风格而言,你应该在覆盖equals()方法时,总是同时覆盖hashCode()方法。
例子:
class SetType { int i; public SetType(int n) { i = n; } @Override public boolean equals(Object o) { return o instanceof SetType && (i == ((SetType) o).i); } @Override public String toString() { return Integer.toString(i); } } class HashType extends SetType { public HashType(int n) { super(n); } @Override public int hashCode() { return i; } } class TreeType extends SetType implements Comparable<TreeType> { public TreeType(int n) { super(n); } public int compareTo(TreeType o) { return (o.i < i ? -1 : (o.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) { } return set; } static <T> void test(Set<T> set, Class<T> type) { fill(set, type); fill(set, type); fill(set, type); System.out.println(set); } public static void main(String[] args) { // 上方的是结果 // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] test(new HashSet<HashType>(), HashType.class); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] test(new LinkedHashSet<HashType>(), HashType.class); // [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 倒序排序 test(new TreeSet<TreeType>(), TreeType.class); // [7, 3, 1, 9, 6, 5, 9, 1, 3, 8, 9, 0, 5, 2, 6, 1, 5, 4, 2, 0, 7, 4, 3, // 8, 8, 7, 6, 4, 0, 2] test(new HashSet<SetType>(), SetType.class); // [8, 0, 3, 9, 0, 2, 8, 5, 2, 3, 4, 1, 2, 9, 4, 1, 9, 6, 7, 7, 4, 1, 3, 8, 6, 5, 7, 5, 6, 0] test(new HashSet<TreeType>(), TreeType.class); // [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] test(new LinkedHashSet<SetType>(), SetType.class); // [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] test(new LinkedHashSet<TreeType>(), TreeType.class); try { test(new TreeSet<SetType>(), SetType.class); } catch (Exception e) { System.out.println(e.getMessage()); } } }注意,在compareTo(),我没有使用“简洁明了”的形式return i - i2;因为这是一个常见的编程错误,它只有在i和i2都是无符号的int(如果Java确实有unsigned关键字的话,但实际上并没有)时才能正确工作。对于java的有符号int,它就出错,因为int不够大,不足以表现两个有符号的int的差。例如i是很大的正整数,而j是很大的负整数,i-j就会溢出并且返回负值,这就不正确了。
时间: 2024-10-11 17:51:29