Set和存储顺序
Set和存储顺序在java中使用set容器存储时,除非是使用了诸如Integer和String 的java预定义的类型,这些类型是被设计可以在容器内部使用的。当我们自己创建类型时,我们需要怎么样的形式来维护存储顺序呢?其实在不同的Set实现是具有不同的行为,所以对于在特定的Set实现中,放置的类型也有不同的要求。举例类型规定Set存入Set的每个元素都必须是唯一的,因为...
Set和存储顺序
在java中使用set容器存储时,除非是使用了诸如Integer和String 的java预定义的类型,这些类型是被设计可以在容器内部使用的。当我们自己创建类型时,我们需要怎么样的形式来维护存储顺序呢?其实在不同的Set实现是具有不同的行为,所以对于在特定的Set实现中,放置的类型也有不同的要求。
举例
类型 | 规定 |
---|---|
Set | 存入Set的每个元素都必须是唯一的,因为Set不保存相同的元素。加入Set的元素必须实现equals()方法来确保对象的唯一性。Set与Collection有完全一样的接口,Set接口不保证维护元素的次序。 |
HashSet | 为快速查找而设计的Set。存入HashSet的元素必须定义hashCode() |
TreeSet | 为保证次序的Set,底层是树的结构。使用它可以从Set中提取有序的序列。元素必须实现Comparable接口 |
LinkedList | 具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的顺序),必须实现hashCode()方法。 |
下面的实例演示为了成功使用特定的Set实现类型而必须定义的方法:
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
/**
* @fileName:TypeOfSets
* @author:ccl
* @createTime:2019-05-17
*/
class SetType{
int i;
public SetType(int i){
this.i = i;
}
// 重写 equals方法
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 i) {
super(i);
}
//定义hashCode
public int hashCode(){
return i;
}
}
//有序Set
class TreeType extends SetType implements Comparable<TreeType>{
public TreeType(int i) {
super(i);
}
@Override
public int compareTo(TreeType treeType) {
//不建议使用i-i2 因为很可能溢出
return treeType.i<i?-1:(treeType.i==i?0:1);
}
}
public class TypeOfSets {
static <T> Set<T> fill(Set<T> set,Class<T> type){
for(int i=0;i<10;i++){
try {
set.add(type.getConstructor(int.class).newInstance(i));
} catch (Exception e) {
e.printStackTrace();
}
}
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) {
//学习的话 记得打断点
//先走我们自定义的hashCode,如果set中有重复哈希值,那么再走equals方法
test(new HashSet<HashType>(),HashType.class);
//先走我们自定义的hashCode,如果set中有重复哈希值,那么再走equals方法
test(new LinkedHashSet<HashType>(),HashType.class);
// 此处我定义为降序
test(new TreeSet<TreeType>(),TreeType.class);
// 大家思考此处为什么会有重复值呢?toString的话
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 {
//报异常 SetType cannot be cast to java.lang.Comparable
test(new TreeSet<SetType>(),SetType.class);
}catch (Exception e){
System.out.println(e.getMessage());
}
try {
//报异常 SetType cannot be cast to java.lang.Comparable
test(new TreeSet<HashType>(),HashType.class);
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
运行结果
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
[7, 0, 7, 6, 9, 3, 4, 6, 0, 0, 8, 7, 1, 1, 8, 5, 5, 3, 2, 4, 1, 3, 6, 4, 9, 2, 5, 2, 9, 8]
[7, 5, 6, 4, 3, 9, 0, 8, 1, 5, 8, 1, 0, 7, 6, 2, 5, 0, 4, 3, 1, 9, 2, 7, 6, 9, 4, 8, 2, 3]
[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: SetType cannot be cast to java.lang.Comparable
总结
基类SetType只存储了一个int,并且通过toString()方法产生它的值。因为在Set中存储的类必须具有equals()方法。其等价性基于这个int类型的 i决定。
HashType实现了Comparable接口,没有使用简洁明了的return i-i2,因为他只有在i和i2都为无符号int时才正确(假如有unsigned关键字),因为假如i是很大的正整数,而i2是个很大的负整数,那么其差值会溢出并且产生负值。
如果我们尝试在TreeSet中使用没有实现的Comparable的类型,那么将抛出异常。
思考答案 因为会调用默认的hashCode方法,这是合法的行为,即使它对于你的结果是不正确的。
更多推荐
所有评论(0)