Java集合(十一)TreeSet解读

向TreeSet中添加的数据,要求的是相同类的对象

两种排序方式:自然排序和定制排序

 我们查看treeset的构造器的时候,发现TreeSet的构造器除了那种无参构造器之外,还提供了另外四种。其中TreeSet(Comparator<E>)可以传比较器。

public interface Comparator<T> {
 int compare(T o1, T o2);

Comparator是一个接口,他有一个方法叫compare,我们可以在他的compare方法指定他的一个排序规则。

我们在设计代码过程中用到的compareTo方法的源码如下所示:

 public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }

就是把value值取出来,然后进行循环比较,如果发现不相等,就返回c1-c2,然后谁的ACCIS码值大,就返回大于0的值,否则返回小于0的值。然后根据返回的值进行排序。

我们设计代码如下所示:

package com.rgf.set;

import java.util.Comparator;
import java.util.TreeSet;

@SuppressWarnings({"all"})
public class TreeSet_ {
    public static void main(String[] args) {
        //1.当我们使用无参构造器,创建TreeSet时,仍然是无序的。
        //2.我们希望添加的元素按照字符串大小来排序
        //3.使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类)
        //并指定排序规则

        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //调用String的compareTo方法,进行字符串大小比较
                return ((String)o1).compareTo((String)o2);
            }
        });
        //添加数据
        treeSet.add("jack");
        treeSet.add("tom");
        treeSet.add("sp");
        treeSet.add("a");

        System.out.println("treeSet="+treeSet);
    }
}

运行界面如下所示:

 这种排序是从大到小排,我们也可以将他拍成从小到大排:

 return ((String)o2).compareTo((String)o1);

运行之后如下所示:

 我们下来进行源码的分析:

我们在 TreeSet treeSet = new TreeSet(new Comparator() {代码处进行断点的设置:

 我们点击Force Step进入之后,在退出来。

我们可以通过f9直接跳出来,我们也可以进去后再退出来,我们进行如下所示:

 这个构造器接收了一个comparator,这个接口的实现对象。

public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

我们继续进去如下所示:他把这个对象传给了如下所示:

 这个comparator(比较器)传给了底层的TreeMap,

我们继续进行下一步,我们进入了add方法:

 我们继续进行下一步:

我们再次进入了TreeMap里面了,进入了TreeMap里面的put方法。

 我们继续进行下一步:

  


//我们将comparator赋给了cpr,comparator为我们传进去的comparator。 
Comparator<? super K> cpr = comparator;
//在调用treeSet.add("tom"),在底层会执行到
        if (cpr != null) {//是有比较器的,比较器不为空。cpr,就是我们的匿名内部类(对象)
            do {
                parent = t;
                //动态绑定到我们的匿名内部类(对象)compare方法
                cmp = cpr.compare(key, t.key); 
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else  //如果相等,即返回0,这个key就没有加入
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

我们继续进行下一步之后,确实进入到如下所示:

 我们进行设计重复值如下所示:

package com.rgf.set;

import java.util.Comparator;
import java.util.TreeSet;

@SuppressWarnings({"all"})
public class TreeSet_ {
    public static void main(String[] args) {
        //1.当我们使用无参构造器,创建TreeSet时,仍然是无序的。
        //2.我们希望添加的元素按照字符串大小来排序
        //3.使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类)
        //并指定排序规则

        TreeSet treeSet = new TreeSet(new Comparator() {
            //实现了把匿名对象传给了TreeSet的底层TreeMap的Comparator的一个属性。
            @Override
            public int compare(Object o1, Object o2) {
                //调用String的compareTo方法,进行字符串大小比较
                return ((String)o2).compareTo((String)o1);
            }
        });
        //添加数据
        treeSet.add("jack");
        treeSet.add("tom");
        treeSet.add("sp");
        treeSet.add("a");
        treeSet.add("a");
        

        System.out.println("treeSet="+treeSet);
        //1.构造器把传入的比较器对象,赋给了TreeSet的底层的TreeMap的属性this.comparator。
    }
}

我们运行界面如下所示:

 我们发现运行结果里面没有重复值a,我们进行debug,如下所示:

我们进入add方法:

 在进入put方法:

public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
//比较器赋值给cpr.
        Comparator<? super K> cpr = comparator;
//比较器赋值之后,不等于空
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
//我们在有值相同的时候是没有进去的。
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

 再次继续之后,我们发现退出来了:

所以相同的值是没有进去的。

如果我们修改按照长度大小排序:

 我们修改代码如下所示:

//按照长度大小排序:
                return ((String)o2).length()-((String)o1).length();

运行界面如下所示:

如果长度大小从小到大,代码修改如下所示:

//按照长度大小排序:
                return ((String)o1).length()-((String)o2).length();

运行界面如下所示:

我们在添加如如下代码的时候,添加不进去:

package com.rgf.set;

import java.util.Comparator;
import java.util.TreeSet;

@SuppressWarnings({"all"})
public class TreeSet_ {
    public static void main(String[] args) {
        //1.当我们使用无参构造器,创建TreeSet时,仍然是无序的。
        //2.我们希望添加的元素按照字符串大小来排序
        //3.使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类)
        //并指定排序规则

        TreeSet treeSet = new TreeSet(new Comparator() {
            //实现了把匿名对象传给了TreeSet的底层TreeMap的Comparator的一个属性。
            @Override
            public int compare(Object o1, Object o2) {
                //调用String的compareTo方法,进行字符串大小比较
                //按照长度大小排序:
                return ((String)o1).length()-((String)o2).length();
            }
        });
        //添加数据
        treeSet.add("jack");
        treeSet.add("tom");
        treeSet.add("sp");
        treeSet.add("a");
        treeSet.add("rgf");//加入不进去。相同长度会覆盖前面的。一个大小
        //我们所设置的规则为长度相等就是相同的元素。
        //这里不进行替换,这相当于是key,key不允许重复,
        //重新了comparetor,所以他的长度就是key,已经有一个key是3了所以加不进去。


        System.out.println("treeSet="+treeSet);
        //1.构造器把传入的比较器对象,赋给了TreeSet的底层的TreeMap的属性this.comparator。
    }
}

 运行界面如下所示:

 我们将规则进行改变后,如下所示:

package com.rgf.set;

import java.util.Comparator;
import java.util.TreeSet;

@SuppressWarnings({"all"})
public class TreeSet_ {
    public static void main(String[] args) {
        //1.当我们使用无参构造器,创建TreeSet时,仍然是无序的。
        //2.我们希望添加的元素按照字符串大小来排序
        //3.使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类)
        //并指定排序规则

        TreeSet treeSet = new TreeSet(new Comparator() {
            //实现了把匿名对象传给了TreeSet的底层TreeMap的Comparator的一个属性。
            @Override
            public int compare(Object o1, Object o2) {
                //调用String的compareTo方法,进行字符串大小比较
                //按照长度大小排序:
                return ((String)o2).compareTo((String)o1);
            }
        });
        //添加数据
        treeSet.add("jack");
        treeSet.add("tom");
        treeSet.add("sp");
        treeSet.add("a");
        treeSet.add("rgf");//加入不进去。相同长度会覆盖前面的。一个大小
        //我们所设置的规则为长度相等就是相同的元素。
        //这里不进行替换,这相当于是key,key不允许重复,
        //重新了comparetor,所以他的长度就是key,已经有一个key是3了所以加不进去。


        System.out.println("treeSet="+treeSet);
        //1.构造器把传入的比较器对象,赋给了TreeSet的底层的TreeMap的属性this.comparator。
    }
}

运行界面如下所示:

我们发现成功添加了进去。 

我们查看TreeSet里面的比较器:自然排序和定制排序。

自然排序:

package com.rgf.jihe;

import java.util.Iterator;
import java.util.Objects;
import java.util.TreeSet;

/**
 * TreeSet:可以按照添加对象的指定属性,进行排序。(无法进行存放相同的数据,TreeSet和TreeMap采用红黑树的存储结构)
 * 特点:有序,查询速度比List快
 * 1.向TreeSet中添加的数据,要求是相同类的对象。
 * 2.两种排序方式:自然排序(实现Comparable接口)和定制排序。
 * 3.自然排序中,比较两个对象是否相同的标准为:CompareTo()返回0,不再是equals()方法
 *
 */
public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet set = new TreeSet();
   //     set.add(123);
        //不能添加不同类的对象,
        //set.add("ABC"); 进行排序的时候会报错,出现ClassCastException
        /*set.add(456);
        set.add(789);
        set.add(452);*/
        //结果按照从小到大顺序排列的,默认为从小到大排序
       // System.out.println(set);
//set默认排序按照equals,但是treeset就是按照comparable去比较
        set.add(new User("ABC",25));
        set.add(new User("A",21));
        set.add(new User("A",28));
        set.add(new User("AB",26));


        //会出现ClassCastException,没有提示应该按照什么排。
        //首先我们进行自然排序,即我们在该类实现Comparable接口。
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}


class User implements  Comparable{
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
//我们写出来该方法则不会再继续报错。
//按照姓名从大到小排列,年龄从小到大排列
    @Override
    public int compareTo(Object o) {
        if(o instanceof  User){
            User user=(User)o;
            //return -this.name.compareTo(user.name);为了可以确保相同的值可以进行排序,我们进行二级排序
            int compare=-this.name.compareTo(user.name);
            if(compare!=0){//如果相等,则我们进行比较年龄
                return compare;
            }else {
                return Integer.compare(this.age,user.age);
            }
        }else {
            throw  new RuntimeException("输入的类型不匹配");
        }
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (age != user.age) return false;
        return Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}

运行之后如下所示:

定制排序:

package com.rgf.jihe;

import org.junit.Test;

import java.util.Comparator;
import java.util.Iterator;
import java.util.Objects;
import java.util.TreeSet;

/**
 * TreeSet:可以按照添加对象的指定属性,进行排序。(无法进行存放相同的数据,TreeSet和TreeMap采用红黑树的存储结构)
 * 特点:有序,查询速度比List快
 * 1.向TreeSet中添加的数据,要求是相同类的对象。
 * 2.两种排序方式:自然排序(实现Comparable接口)和定制排序(Comparator接口)。
 * 3.自然排序中,比较两个对象是否相同的标准为:CompareTo()返回0,不再是equals()方法
 * 4.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()方法。
 */
public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet set = new TreeSet();
   //     set.add(123);
        //不能添加不同类的对象,
        //set.add("ABC"); 进行排序的时候会报错,出现ClassCastException
        /*set.add(456);
        set.add(789);
        set.add(452);*/
        //结果按照从小到大顺序排列的,默认为从小到大排序
       // System.out.println(set);
//set默认排序按照equals,但是treeset就是按照comparable去比较
        set.add(new User("ABC",25));
        set.add(new User("A",21));
        set.add(new User("A",28));
        set.add(new User("AB",26));


        //会出现ClassCastException,没有提示应该按照什么排。
        //首先我们进行自然排序,即我们在该类实现Comparable接口。
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
    
    @Test
    public  void  test1(){
        Comparator com = new Comparator() {
            //按照年龄从小到大排列
            @Override
            public int compare(Object o1, Object o2) {
             if(o1 instanceof User && o2 instanceof User){
                 User u1=(User)o1;
                 User u2=(User) o2;
                 return Integer.compare(u1.getAge(),u2.getAge());
             }else {
                 throw new RuntimeException("输入的数据类型不匹配");
             }
            }
    };
        TreeSet set = new TreeSet(com);

        set.add(new User("ABC",25));
        set.add(new User("A",21));
        set.add(new User("A",28));
        set.add(new User("AB",26));
        set.add(new User("AB1",26));

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
    }








 class User implements  Comparable{
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
//我们写出来该方法则不会再继续报错。
//按照姓名从大到小排列,年龄从小到大排列
    @Override
    public int compareTo(Object o) {
        if(o instanceof  User){
            User user=(User)o;
            //return -this.name.compareTo(user.name);为了可以确保相同的值可以进行排序,我们进行二级排序
            int compare=-this.name.compareTo(user.name);
            if(compare!=0){//如果相等,则我们进行比较年龄
                return compare;
            }else {
                return Integer.compare(this.age,user.age);
            }
        }else {
            throw  new RuntimeException("输入的类型不匹配");
        }
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (age != user.age) return false;
        return Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}

运行之后如下所示:

 我们的练习如下所示:

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直再追梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值