欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Set和存储顺序

程序员文章站 2022-05-15 22:53:39
...

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,因为他只有在ii2都为无符号int时才正确(假如有unsigned关键字),因为假如i是很大的正整数,而i2是个很大的负整数,那么其差值会溢出并且产生负值
如果我们尝试在TreeSet中使用没有实现的Comparable的类型,那么将抛出异常。

思考答案 因为会调用默认的hashCode方法,这是合法的行为,即使它对于你的结果是不正确的。