小东吖 之 java Set接口 HashSet集合 TreeSet集合

Set不保存重复的元素。Set中最常被使用的是测试归属性,你可以很容易的询问某个对象是否在某个Set中。Set具有与Collection完全一样的接口,因此没有任何额外的功能。实际上Set就是Collection,只是行为不同。

  实现了Set接口的主要有HashSet、TreeSet、LinkedHashSet这几个共同点就是每个相同的项只保存一份。他们也有不同点,区别如下:

一.HashSet

主要用来去重
|–HashSet:底层数据结构是哈希表。是线程不安全的。不同步。
HashSet是如何保证元素唯一性的呢?

是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。
如果元素的hashcode值不同,不会调用equals。

  HashSet使用的是相当复杂的方式来存储元素的,使用HashSet能够最快的获取集合中的元素,效率非常高(以空间换时间)。会根据hashcode和equals来庞端是否是同一个对象,如果hashcode一样,并且equals返回true,则是同一个对象,不能重复存放。
所以一般都是用来去重的

public class Demo01 {
    public static void main(String[] args) {
        fun1();
        fun2();
}
public static void fun1() {
        // 创建一个HashSet集合 保存两个a b c d
        // 并打印
        HashSet<String> hashSet = new HashSet<>();
        hashSet.add("a");
        hashSet.add("a");
        hashSet.add("b");
        hashSet.add("b");
        hashSet.add("d");
        hashSet.add("d");
        hashSet.add("c");
        hashSet.add("c");

        // 使用增强for循环遍历
        for (String string : hashSet) {
            System.out.println(string);
        }
    }


    public static void fun2() {
        // 需求 创建一个HashSet 保存 5个人 并遍历
        HashSet<Person> hashSet = new HashSet<>();
        hashSet.add(new Person("q", 12));
        hashSet.add(new Person("q", 12));
        hashSet.add(new Person("e", 15));
        hashSet.add(new Person("e", 15));
        hashSet.add(new Person("v", 11));

        Iterator<Person> iterator = hashSet.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

}

结果

a
b
c
d

看上述的例子 可看到fun1的方法 可以成功去除重复元素 fun2就不可以了

因为:

在java 中字符串对象是一种特殊的对象.String 类是一个不可变的类..也就说,String 对象
一旦创建就不允许修改。String 类有一个对应的 String 池,也就是 String pool.每一个内容相同的字符串对象都对应于一个 pool 里的对象. 所以在hashSet中存储两个内容相同的String,实际上他们都是对应于一个pool里的对象。所以说不是hashset可以识别string的唯一性,而是内容相同的String他们其实就是一个对象。在不允许重复的set中自然是不允许同一个对象存在两次的。

而把对象装进去呢 是因为每次都是new一次 创建一个新对象 哈希值就会不一样 就不会调用equals方法去比较 直接存进去了 这个时候需要在Student类中去重写hashCode和equals方法

二.TreeSet

主要用来排序
TreeSet也不能存放重复对象,但是TreeSet会自动排序,如果存放的对象不能排序则会报错,所以存放的对象必须指定排序规则。排序规则包括自然排序和客户排序。

  ①自然排序:TreeSet要添加哪个对象就在哪个对象类上面实现java.lang.Comparable接口,并且重写comparaTo()方法,返回0则表示是同一个对象,否则为不同对象。

  ②自定义排序:建立一个第三方类并实现java.util.Comparator接口。并重写方法。定义集合形式为TreeSet ts = new TreeSet(new 第三方类());

/**
     * 创建一个TreeSet集合 添加几个数 查看效果
     */
    public static void fun1() {
        int[] array = {1,5,78,3,32,5,7,7};
        TreeSet<Integer> treeSet = new TreeSet<>();
        treeSet.add(1);
        treeSet.add(3);
        treeSet.add(2);
        treeSet.add(3);
        treeSet.add(4);

        System.out.println(treeSet);
    }

/**
     * 创建一个TreeSet 添加4个人
     */
    public static void fun2() {
        // 创建一个TreeSet 添加4个人
        TreeSet<Person> treeSet = new TreeSet<>();
        treeSet.add(new Person("周五", 12));
        treeSet.add(new Person("李四", 12));
        treeSet.add(new Person("小三", 11));
        treeSet.add(new Person("小三", 16));

        System.out.println(treeSet);
    } 


}

结果

[1, 2, 3, 4]

fun1成功打印出来 因为都是常量 fun2就不行 他是个对象 想要排序 要实现compareble接口 重写compareTo的方法
TreeSet的第一种排序方式是实现Comparable接口覆写compareTo方法,此排序是自然顺序排序。

TreeSet的第二种排序方式是实现Comparator接口覆写compare方法,此排序是在集合一初始化就拥有的,所以要讲此接口的实例对象传入到TreeSet的构造函数中。

3.TreeSet总结
1). TreeSet的特点
(1). 可以对元素进行排序
有两种排序方式。
(2). TreeSet保证元素的唯一性依据
在实现的Comparable的compareTo或者Comparator的compare方法中,如果这两个方法的返回值为0,那么TreeSet就认为这两个元素一样。按照Set的唯一性规则,在一次重复的元素不能被添加到TreeSet这个集合中。
2). TreeSet的两种排序方式
(1). 让元素本身具有比较性
元素本身要实现Comparable接口并实现里面的compareTo方法以保证元素本身具有比较性
(2). 让容器自身具有比较性
当元素本身不具有比较性或者具备的比较性不是所需要的,就在TreeSet建立实例的时候,传入Comparator接口的实现子类的实例。这个Comparator子类必须实现compare方法。
(3). 元素的可存储性
[1]. 自定义的类,如果重写了hashCode和equals两个方法,可以存入HashSet容器
[2]. 自定义的类,如果实现了Comparable的compareTo()方法,可以存入TreeSet容器。
【总结】如果自定义的类既重写了hashCode和equals,又实现了compareTo,那么这个类的元素既可以存入HashSet容器,也可以存入TreeSet容器。

三.比较器

当元素自身不具备比较性,或者具备的比较性不是所需要的。
这时需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。

// 创建一个比较器的类 实现比较器接口中的方法
public class ComparatorImpl implements Comparator<String>{
    // 主要按字符串长度比较
    // 次要按字符比
    // 比较器相当于TreeSet
    @Override
    public int compare(String o1, String o2) {
        int length = o1.length() - o2.length();
        int num = length == 0 ? o1.compareTo(o2) : length;
        // 如果字符串长度和字符都相同 也需要把字符串存进去
        // 所以返回1 或 -1(不返0就行)
        return num == 0 ? 1 : num;
    }



}
public static void fun3() {
        // 比较器
        // 集合中保存字符串 按字符串长度排序
        // 初始化TreeSet的同时把比较器穿进去 传入TreeSet中去
        TreeSet<String> treeSet = new TreeSet<>(new ComparatorImpl());
        treeSet.add("aaa");
        treeSet.add("bb");
        treeSet.add("cccc");
        treeSet.add("z");
        treeSet.add("bb");

        for (String string : treeSet) {
            System.out.println(string);
        }
        System.out.println(treeSet);
    }

java中treeset使用Comparator进行比较的三种方法

1.让元素具备比较性。

比如我们比较两个人。我们定义一个person类,并且实现Comparable接口
例:
public class Person implements Comparable{
private int age;
private String name;

public Person(){}

public Person(int age, String name) {
    this.age = age;
    this.name = name;
}

public int getAge() {
    return age;
}

public String getName() {
    return name;
}

public void setAge(int age) {
    this.age = age;
}
public void setName(String name) {
    this.name = name;
}

@Override
public int compareTo(Person o) {
    int number = this.age - o.getAge();
    // 如果年龄相同去看姓名 
    // 如果年龄不同直接返回年龄的差
    return number ==0 ? this.name.compareTo(o.getName()) :number;

}

2.第二种是写个类实现Comparator接口

例:

     class myComparator implements Comparator{

    @Override
    public int compare(Object o1, Object o2) {
        Person p1 = (Person) o1;
        Person p2 = (Person) o2;

        int num = p1.getName().compareTo(p2.getName());
      // 0的话是两个相同,进行下一个属性比较
        if (num == 0){
            return new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
        }

        return num;
    }
}

然后在new Set的时候放进去。如
TreeSet ts = new TreeSet(new myComparator());

3.第三种写内名内部类方法如:

    TreeSet ts = new TreeSet(new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            Person p1 = (Person) o1;
            Person p2 = (Person) o2;

            int num = p1.getName().compareTo(p2.getName());

            if (num == 0){
                return new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
            }

            return num;

        }
    });

四.LinkedHashSet

LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();

        linkedHashSet.add("a");
        linkedHashSet.add("a");
        linkedHashSet.add("c");
        linkedHashSet.add("c");
        linkedHashSet.add("b");
        linkedHashSet.add("b");
        linkedHashSet.add("d");
        linkedHashSet.add("d");

        System.out.println(linkedHashSet);
    }

结果

[a, c, b, d]

通过结果会发现 LinkedHashSet是有序 去重复的
LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值