TreeSet详解

Posted 午觉千万别睡过

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TreeSet详解相关的知识,希望对你有一定的参考价值。

TreeSet详解

TreeSet的基本操作

放到TreeSet集合中的元素: 无序不可重复,但是可以按照元素的大小顺序自动排序

 		// 集合的创建
        TreeSet<Integer> ts = new TreeSet<>();
        // 添加元素
        ts.add(1);
        ts.add(100);
        ts.add(10);
        ts.add(0);
        ts.add(15);
		//迭代器遍历
        Iterator<Integer> it = ts.iterator();
        while(it.hasNext())
            Integer next = it.next();
            System.out.println(next);
        
		//增强for循环遍历
        for (Integer i:
             ts) 
            System.out.println(i);
        

运行结果:

0
1
10
15
100
  1. TreeSet集合底层实际上是一个TreeMap,而TreeMap集合底层是一个二叉树,也将TreeSet集合中的元素称为可排序组合

应用场景之一: 在页面展示用户信息的时候按照生日升序或者降序

	数据库中有很多数据:
    userid  name    birth
    -----------------------
    1       zs      1980-11-11
    2       ls      1980-10-11
    3       ww      1981-11-11
    4       zl      2001-12-23
    编写程序从数据库当中取出数据,在页面展示用户信息的时候按照生日升序或者降序。
    这个时候可以使用TreeSet集合,因为TreeSet集合放进去,拿出来是有序的
TreeSet集合,对自定义类型不能进行排序!!!

example:

public class TreeSetTest03 

    public static void main(String[] args) 

        Person p1 = new Person(32);
        Person p2 = new Person(20);
        Person p3 = new Person(25);
        TreeSet<Person> ts = new TreeSet<>();
        ts.add(p1);
        ts.add(p2);
        ts.add(p3);
        for (Person x:
             ts) 
            System.out.println(x);
        
    


class Person
    int age;
    public Person(int age)
        this.age = age;
    
    // 重写toString方法
    @Override
    public String toString() 
        return "Person" +
                "age=" + age +
                '';
    

运行结果:

Exception in thread "main" java.lang.ClassCastException: com.lxz.review1.Person cannot be cast to java.lang.Comparable
	at java.util.TreeMap.compare(TreeMap.java:1294)
	at java.util.TreeMap.put(TreeMap.java:538)
	at java.util.TreeSet.add(TreeSet.java:255)
	at com.lxz.review1.TreeSetTest03.main(TreeSetTest03.java:23)

程序运行结果抛出了一个异常:Person cannot be cast to java.lang.Comparable.程序无法进行排序是因为没有指定自定义类型Person对象之间的比较规则,谁大谁小没有说明!

在TreeSet中实现自定义类型的排序有两种方法
  1. 方式一:放在TreeSet集合中的元素需要实现java.lang.Comparable接口
public class TreeSetTest04 

    public static void main(String[] args) 

        Person1 p1 = new Person1(32);
        Person1 p2 = new Person1(20);
        Person1 p3 = new Person1(25);
        TreeSet<Person1> ts = new TreeSet<>();
        ts.add(p1);
        ts.add(p2);
        ts.add(p3);
        for (Person1 x:
                ts) 
            System.out.println(x);
        
    



/**
 * 放在TreeSet集合中的元素需要实现java.lang.Comparable接口
 * 并且实现compareTo方法。equals可以不写
 */
class Person1 implements  Comparable<Person1>
    int age;
    public Person1(int age)
        this.age = age;
    
    // 重写toString方法
    @Override
    public String toString() 
        return "Person1" +
                "age=" + age +
                '';
    
    /**
     * 需要在这个比较的方法中编写比较的逻辑或者比较的规则,按照什么进行比较
     * 拿着参数k和集合中的每一个key进行比较,返回值可能是大于0 小于0 或者等于0
     * 比较规则最终还是由程序员指定的; 例如按照年龄升序,或者按照年龄降序。
     * @param o
     * @return
     */
    @Override
    public int compareTo(Person1 o)  // c1.compareTo(c2)
        return this.age-o.age; // >0 =0 <0 都有可能
    

  1. 方式二:在构造器TreeSet或者TreeMap集合的时候给它传一个比较器对象
public class TreeSetTest06 

    public static void main(String[] args) 
        // 此时创建TreeSet集合的时候,需要使用这个比较器。
        // TreeSet<WuGui> wuGui = new TreeSet<>(); // 这样不行,没有通过构造方法传递一个比较器进去。
        // 给构造方法传递一个比较器
        TreeSet<WuGui> wuGui = new TreeSet<>(new WuGuiComparator()); // 底层源码可知其中一个构造方法的参数为比较器
        // 大家可以使用匿名内部类的方式
        wuGui.add(new WuGui(100));
        wuGui.add(new WuGui(10));
        wuGui.add(new WuGui(1000));
        for (WuGui wugui:
             wuGui) 
            System.out.println(wugui);
        
    

class WuGui 
    int age;
    public WuGui(int age) 
        this.age = age;
    
    @Override
    public String toString() 
        return "WuGui" +
                "age=" + age +
                '';
    

// 单独再这里编写一个比较器
// 比较器实现java.util.Comparator接口 (Comparable是java.lang包下的。Comparator是java.util包下的。)
class WuGuiComparator implements Comparator<WuGui>
    @Override
    public int compare(WuGui o1, WuGui o2) 
        // 指定比较规则
        // 按照年龄排序
        return o1.age-o2.age;
    

note:

  1. 比较规则经常变换: Comparator 接口的设计符合OCP原则(可切换)
  2. 比较规则较固定: Comparable

example: 先按照年龄升序排序,如果年龄一样再按照姓名升序排序(采用实现Comparable接口的方法)

public class TreeSetTest05 
    public static void main(String[] args) 
        TreeSet<Vip> vips = new TreeSet<>();
        vips.add(new Vip("zhangsi",20));
        vips.add(new Vip("zhangsan",20));
        vips.add(new Vip("liuxiangzheng",18));
        for (Vip vip:
             vips) 
            System.out.println(vip);
        
    

class Vip implements Comparable<Vip>
    String name;
    int age;

    public Vip(String name,int age)
        this.name = name;
        this.age = age;
    
    @Override
    public String toString() 
        return "Vip" +
                "name='" + name + '\\'' +
                ", age=" + age +
                '';
    

    @Override
    public int compareTo(Vip o) 
        if(this.age==o.age)
            // 年龄一样按照姓名升序来
            // note: this.name 是字符串,String实现了compareTo方法,故直接调用即可
            return this.name.compareTo(o.name);
        else
            // 年龄不一样
            return this.age-o.age;
        
    

总结:

  1. 对于自定义的类型,想要在TreeSet中实现排序,必须实现Comparable接口或者编写Comparator比较器,制定其比较规则!JDK自己提供的数据类型不用程序员实现Comparable接口或者编写Comparator比较器是因为它的底层源码都实现了Comparable接口,具有了比较规则,故可以排序!
  2. Comparable接口中重写的CompareTo方法的返回值很重要:
返回0表示相同,value会覆盖
返回>0,会继续在右子树上找
返回<0,会继续在左子树上找
  1. TreeSet底层原理:
  • TreeSet/TreeMap底层都采用的是自平衡二叉树(TreeSet底层是TreeMap): 遵循左小右大的原则存放,存放的过程也就是排序的过程

  • 遍历二叉树的三种方式: (note:左永远在右的左边)

    • 前序遍历: 根左右
    • 中序遍历:左根右 (满足自平衡二叉树的存放方式,中序遍历取出数据的时候就为自动排序好的数据)
    • 后序遍历:左右根
  • TreeSet/TreeMap集合采用的是:中序遍历

  • 二叉树的遍历均可以看成是递归的过程,也就是将一个树不断的划分成左子树、根、右子树的过程,直到不能再划分成一个子树

TreeSet()详解

 

1.TreeSet原理:
   /*
 * TreeSet存储对象的时候, 可以排序, 但是需要指定排序的算法
 * 
 * Integer能排序(有默认顺序), String能排序(有默认顺序), 自定义的类存储的时候出现异常(没有顺序)
 * 
 * 如果想把自定义类的对象存入TreeSet进行排序, 那么必须实现Comparable接口
 *   在类上implement Comparable
 *   重写compareTo()方法
 *   在方法内定义比较算法, 根据大小关系, 返回正数负数或零
 *   在使用TreeSet存储对象的时候, add()方法内部就会自动调用compareTo()方法进行比较, 根据比较结果使用二叉树形式进行存储
 */

2.TreeSet是依靠TreeMap来实现的。
TreeSet是一个有序集合,TreeSet中的元素将按照升序排列,缺省是按照自然排序进行排列,意味着TreeSet中的元素要实现Comparable接口。或者有一个自定义的比较器。
我们可以在构造TreeSet对象时,传递实现Comparator接口的比较器对象。

 
import java.util.Iterator;
import java.util.*;

public class TreeSetTest {
    public static void main(String[] args) {
        Set ts = new TreeSet();
        ts.add("abc");
        ts.add("xyz");
        ts.add("rst");
        Iterator it = ts.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}
 

输出结果:

abc

rst

xyz

打印结果不是和先前加入的顺序一样,它是按照一个字母的排序法进行排序的。这是因为String 类实现了Comparable接口。

如果我们自己定义的一个类的对象要加入到TreeSet当中,那么这个类必须要实现Comparable接口。

 
package test.treeset;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;


public class test_treeset {
     @SuppressWarnings("unchecked")
    public static void main(String[] args) {
            Set ts = new TreeSet();
            ts.add(new Teacher("zhangsan", 1));
            ts.add(new Teacher("lisi", 2));
            ts.add(new Teacher("wangmazi", 3));
            ts.add(new Teacher("wangwu",4));
            ts.add(new Teacher("mazi", 3));
            Iterator it = ts.iterator();
            while (it.hasNext()) {
                System.out.println(it.next());
            }
        }
}
class Teacher implements Comparable {
    int num;
    String name;

    Teacher(String name, int num) {
        this.num = num;
        this.name = name;
    }

    public String toString() {
        return "学号:" + num + "\\t\\t姓名:" + name;
    }

    //o中存放时的红黑二叉树中的节点,从根节点开始比较
    public int compareTo(Object o) {
        Teacher ss = (Teacher) o;
        int result = num < ss.num ? 1 : (num == ss.num ? 0 : -1);//降序
        //int result = num > ss.num ? 1 : (num == ss.num ? 0 : -1);//升序
        if (result == 0) {
            result = name.compareTo(ss.name);
        }
        return result;
    }
}
 

运行结果:

学号:4 姓名:wangwu
学号:3 姓名:mazi
学号:3 姓名:wangmazi
学号:2 姓名:lisi
学号:1 姓名:zhangsan

3.比较器

在使用Arrays对数组中的元素进行排序的时候,可以传递一个比较器。

在使用Collections对集合中的元素进行排序的时候,可以传递一个比较器。

那么在使用TreeSet对加入到其中的元素进行排序的时候可以传入一个比较器吗?

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

通过查看它的构造方法就知道可以传入一个比较器。

构造一个新的空TreeSet,它根据指定比较器进行排序。插入到该 set 的所有元素都必须能够由指定比较器进行相互比较:对于 set 中的任意两个元素 e1 和e2,执行 comparator.compare(e1, e2) 都不得抛出 ClassCastException。如果用户试图将违反此约束的元素添加到 set 中,则 add 调用将抛出 ClassCastException。

 
package test.treeset;

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

public class TreeSetTest {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        TreeSet ts = new TreeSet(new Teacher2.TeacherCompare());
        ts.add(new Teacher2("zhangsan", 2));
        ts.add(new Teacher2("lisi", 1));
        ts.add(new Teacher2("wangmazi", 3));
        ts.add(new Teacher2("mazi", 3));
        Iterator it = ts.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

class Teacher2 {
    int num;
    String name;

    Teacher2(String name, int num) {
        this.num = num;
        this.name = name;
    }

    public String toString() {
        return "学号:" + num + "    姓名:" + name;
    }

    static class TeacherCompare implements Comparator {// 老师自带的一个比较器
        //o1中存放的事目标节点
        //o2中存放时的红黑二叉树中的节点,从根节点开始比较
        public int compare(Object o1, Object o2) {
            Teacher2 s1 = (Teacher2) o1;// 转型
            Teacher2 s2 = (Teacher2) o2;// 转型
            int result = s1.num > s2.num ? 1 : (s1.num == s2.num ? 0 : -1);
            if (result == 0) {
                result = s1.name.compareTo(s2.name);
            }
            return result;
        }

    }

}
 

运行结果:

学号:1 姓名:lisi
学号:2 姓名:zhangsan
学号:3 姓名:mazi
学号:3 姓名:wangmazi

 

文章转载于:http://www.cnblogs.com/meng72ndsc/archive/2010/12/23/1914477.html

 
 

以上是关于TreeSet详解的主要内容,如果未能解决你的问题,请参考以下文章

Java 详解 JVM 工作原理和流程

JVM详解——垃圾回收算法

(转)Java 详解 JVM 工作原理和流程

(转)Java 详解 JVM 工作原理和流程

(转)Java 详解 JVM 工作原理和流程

java集合Set集合之TreeSet详解