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来保存。*