• 17、集合框架_HashSet\TreeSet\比较器\泛型


    一、Set接口中的实现类

    • Set接口存储一组唯一,无序的对象
    • (存入和取出的顺序不一定一致)
    • 操作数据的方法与List类似,Set接口不存在get()方法

    ▪HashSet:采用Hashtable哈希表存储结构
      –优点:添加速度快,查询速度快,删除速度快
      –缺点:无序
      –LinkedHashSet
        ▪ 采用哈希表存储结构,同时使用链表维护次序
        ▪有序(添加顺序)
    ▪TreeSet
      –采用二叉树(红黑树)的存储结构
      –优点:有序(排序后的升序)查询速度比List快
      –缺点:查询速度没有HashSet快

    1、Hash表原理

    public class SetDemo implements Comparator<Person> {
        public static void main(String[] args) {
            Set set = new HashSet();
            set.add("123");
            set.add(1);
            set.add(true);
            set.add("123");
            System.out.println(set);
    }

    源码:

     

     

     HashSet的本质其实是HashMap

     
    public class SetDemo {
        public static void main(String[] args) {
            Set set = new HashSet();
            set.add("123");
            set.add(1);
            set.add(true);
            set.add("123");
            System.out.println(set);
            Iterator iterator = set.iterator();
            while (iterator.hasNext()){
                System.out.println(iterator.next());
            }
            System.out.println("---------");
            //将while循环改成for循环,推荐使用
            for(Iterator iter = set.iterator(); iter.hasNext();){
                System.out.println(iter.next());
            }
        }
    }

    打印结果为:

    /*
    [1, 123, true]
    1
    123
    true
    ---------
    1
    123
    true
    
    Process finished with exit code 0
    */

    二、Treeset

    public class SetDemo {
        public static void main(String[] args) {
            TreeSet treeSet = new TreeSet();
            treeSet.add("123");
            treeSet.add(1);
            treeSet.add("a");
            System.out.println(treeSet);
        }
    }

    报错:

     为什么会报错,需要了解一下TreeSet的数据结构

    源码

    TreeSet本质上是TreeMap 

     TreeMap

     

     用的是红黑数

    /*
    基础知识补充
    BST树:
        二叉搜索比(Binary Search Tree, 简写BST),又称为二叉排序树,属于树的一种.通过二叉树将数据组织起来,树的每个
    节点包含了键值 Key、数据值 data、左子节点指针、右子节点指针.其中键值key是最核心的部分,它的值次定了树的
    组织形状; 数据值data是该节点对应的数据, 有些场景可以忽略,举个例子, key 为身份证号而data人名,通过身份证号找人名;左子节点指针指向左子节点,右子节点指针指向右子节点。
    特点:
        左右子树也分别是二又搜索树.
        左子树的所有节点key值都小于它的根节点的key值
        右子树的所有节点key值都大于它的根节点的key值
        二又搜索树可以为一颗空树.
        一般来说.树中的每个节点的key值都不相等,唱根据需要也可以将相同的key值插入树中
    AVL树:
        AVL树.也称平衡二叉搜索树.AVL树属于树的一种.而且它也是一颗二叉搜索树.不同的是
    他通过一定机制能保证二叉搜索树的平衡,平衡的二叉搜索树的查询效率更高
    特点:
    AVL树是一颗二叉搜索树.
    AVL树的左右子书点也是AVL树.
    AVI树拥有二叉树的所有基本特点.
    每个节点的左右子节点的高度之差的绝对值最多为1,即平衡因子的范围为[-1,1]
    红黑树
    红黑(Red-black)树
        是一种自平衡二叉查找树,它与AVL树类似.都在插入和删除操作时能通过旋转操作保持二叉查找树的平衡,以便能获取高效的查找性能。它可以在O(logn)时间内查找,插入和删除等操作。红黑树是2-3-4树的一种等同,但有些红黑树设定只能左边是红树,这种情况就是2-3树的一种等同了。对于AVL树来说,红黑树牺牲了部分平衡性。
    特点:
        节点是红色或黑色.
        根节点是黑也.
        每个叶节点(NIL节点)是黑色的.
        每个红色节点的两个子节点都为黑色.(从每个叶子到根的所有路径上不能有两个连续的红色节点
        从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
        最长路径不超过最短路径的2倍
    */

    左子树小于根,右子树大于根,有一个自动排序的过程

    public class SetDemo {
        public static void main(String[] args) {
            TreeSet treeSet = new TreeSet();
            treeSet.add(31);
            treeSet.add(1);
            treeSet.add(6);
            System.out.println(treeSet);
        }
    }

    打印结果为:

    /*
    [1, 6, 31]
    
    Process finished with exit code 0
    */

    Set

    public class Person implements Comparable{
        private String name;
        private int age;
    
        public Person(){
    
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        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 boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age &&
                    Objects.equals(name, person.name);
        }
    
        @Override
        public int hashCode() {
    
            return Objects.hash(name, age);
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    public class SetDemo  {
        public static void main(String[] args) {
            TreeSet treeSet = new TreeSet();
            treeSet.add(34);
            treeSet.add(1);
            treeSet.add(65);
            System.out.println(treeSet.ceiling(1));
            System.out.println(treeSet);
            HashSet hashSet = new HashSet();
            hashSet.add(new Person("zhangsan",12));
            hashSet.add(new Person("zhangsan",12));
            hashSet.add(new Person("lisi",13));
            System.out.println(hashSet);
        }
    
    }

    会打印两个,因为是Set

    /*
    ▪ 总结: 
    ▪ HashSet是如何保证元素的唯一性的呢?
    ▪ 答:是通过元素的两个方法,hashCode和equals方法来完成 ▪ 如果元素的HashCode值相同,才会判断equals是否为true ▪ 如果元素的hashCode值不同,不会调用equals方法
    */
    ▪ TreeSet
      – 采用二叉树(红黑树)的存储结构
      – 优点:有序(排序后的升序)查询速度比List快
      – 缺点:查询速度没有HashSet快

    1、Comparable接口

    /*
    * 比较器分类:
    *         内部比较器
    *               定义在元素的类中,通过实现comparable接口来进行实现
    *         外部比较器
    *               定义在当前类中,通过实现comparator接口来实现,但是要将该比较器传递到集合中
    *         注意:外部比较器可以定义成一个工具类,此时所有需要比较的规则如果一致的话,可以复用,而
    *               内部比较器只有在存储当前对象的时候才可以使用
    *               如果两者同时存在,使用外部比较器
    *               当使用比较器的时候,不会调用equals方法
    * */
    树中的元素是要默认进行排序操作的,如果是基本数据类型,自动比较,如果是引用类型的话,需要自定义比较器

    1.1、内部比较器

    • 所有可以“排序”的类都实现了java.lang.Comparable 接口,
    Comparable接口中只有一个方法
    public int compareTo(Object obj);
    public class Person implements Comparable{
        private String name;
        private int age;
    
        public Person(){
    
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        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 boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age &&
                    Objects.equals(name, person.name);
        }
    
        @Override
        public int hashCode() {
    
            return Objects.hash(name, age);
        }
    
        /**
         * 此比较器按照name的长度来进行比较
         * @param o
         * @return
         */
        @Override
        public int compareTo(Object o) {
            Person p  = (Person) o; // 强制转换
            if (p.name.length()>this.name.length()){
                return -1;
            }else if(p.name.length()<this.name.length()){
                return 1;
            }else{
                return 0;
            }
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    public class SetDemo {
        public static void main(String[] args) {
            TreeSet treeSet = new TreeSet();
            treeSet.add(new Person("lisi",15));
            treeSet.add(new Person("wangwu",13));
            treeSet.add(new Person("maliu",12));
            treeSet.add(new Person("zhangsan",19));
            treeSet.add(new Person("zhangsan",12));
            System.out.println(treeSet);
        }
    }

    1.2、外部比较器

    public class Person{
        private String name;
        private int age;
    
        public Person(){
    
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        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 boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age &&
                    Objects.equals(name, person.name);
        }
    
        @Override
        public int hashCode() {
    
            return Objects.hash(name, age);
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    public class SetDemo implements Comparator<Person> {
        public static void main(String[] args) {
            TreeSet treeSet = new TreeSet(new SetDemo());
            treeSet.add(new Person("lisi",15));
            treeSet.add(new Person("wangwu",13));
            treeSet.add(new Person("maliu",12));
            treeSet.add(new Person("zhangsan",19));
            treeSet.add(new Person("zhangsan",12));
            System.out.println(treeSet);
        }
    
        @Override
        public int compare(Person o1, Person o2) {
            if(o1.getAge()>o2.getAge()){
                return -1;
            }else if(o1.getAge() < o2.getAge()){
                return 1;
            }else{
                return 0;
            }
        }
    }

    三、泛型

     这时就报错了

    list里面有整形、字符串、布尔、自定义对象
    可以放object
    public class FanXingDemo {
        public static void main(String[] args) {
            List list = new ArrayList<String>();
            list.add(1); // new Integer(1)
            list.add("abc");//new String("abc)
            list.add(true);//new Boolean(true)
            list.add(new Person("zhangsan",12).toString());
            System.out.println(list);
    
    //        for(int i = 0;i<list.size();i++){
    //            System.out.println(list.get(i));
    //        }
    
            for(Object iter:list){
                System.out.println(iter);
            }
        }
    }

    打印结果为:

    /*
    [1, abc, true, Person{name='zhangsan', age=12}]
    1
    abc
    true
    Person{name='zhangsan', age=12}
    
    Process finished with exit code 0
    */

    我现在想要的是只拿Person里的

    能强转吗

    public class FanXingDemo {
        public static void main(String[] args) {
            List list = new ArrayList<String>();
            list.add(1); // new Integer(1)
            list.add("abc");//new String("abc)
            list.add(true);//new Boolean(true)
            list.add(new Person("zhangsan",12).toString());
            System.out.println(list);
    
    //        for(int i = 0;i<list.size();i++){
    //            System.out.println(list.get(i));
    //        }
    
            for(Object iter:list){
                Person p = (Person) iter;
                System.out.println(iter);
            }
        }
    }

    报错

    不能进行转换int 类型不能转成persion对象
     
    这种情况
    当做一些集合的统一操作的时候,需要保证集合的类型是统一的,此时需要泛型来进行限制
    /**
     *      优点:
     *          1、数据安全
     *          2、获取数据时效率比较高
     *      给集合中的元素设置相同的类型就是泛型的基本需求
     *       使用:
     *          在定义对象的时候,通过<>中设置合理的类型来进行实现
     */
    public class FanXingDemo {
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("1"); // new Integer(1)
            list.add("abc");//new String("abc")
            list.add("true");//new Boolean(true)
            list.add(new Person("zhangsan",12).toString());
            System.out.println(list);
    
    //        for(int i = 0;i<list.size();i++){
    //            System.out.println(list.get(i));
    //        }
    //
            for(String iter:list){
                System.out.println(iter);
            }
        }
    }

    打印结果为:

    /*
    [1, abc, true, Person{name='zhangsan', age=12}]
    1
    abc
    true
    Person{name='zhangsan', age=12}
    
    Process finished with exit code 0
    */

    四、泛型的高阶应用

    1、泛型类

    /*
    在定义类的时候在类名的后面添加<E,K,V,A,B>,起到占位的作用,类中的方法的返回值类型和属性的类型都可以使用
    E:element K:key V:value A:占位 什么字母无所谓,
    */
    public class FanXingClass<A> {
    
        private int id;
        private A a;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public A getA() {
            return a;
        }
    
        public void setA(A a) {
            this.a = a;
        }
    
        public void show(){
            System.out.println("id : "+id+" ,A : "+a);
        }
    }
    public class FanXingDemo {
        public static void main(String[] args) {
            FanXingClass<String> fxc = new FanXingClass<String>();
            fxc.setA("mashibing");
            fxc.setId(1);
            fxc.show();
    
            FanXingClass<Integer> fxc2 = new FanXingClass<Integer>();
            fxc2.setA(34);
            fxc2.setId(2);
            fxc2.show();
    
            FanXingClass<Person> fxc3 = new FanXingClass<Person>();
            fxc3.setA(new Person("aaa",123));
            fxc3.setId(3);
            fxc3.show();
        }
    }

    打印结果为:

    /*
    id : 1 ,A : mashibing
    id : 2 ,A : 34
    id : 3 ,A : Person{name='aaa', age=123}
    
    Process finished with exit code 0
    */

    返回特定的类型

    public class FanXingClass<A> {
    
        private int id;
        private A a;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public A getA() {
            return a;
        }
    
        public void setA(A a) {
            this.a = a;
        }
    
        public void show(){
            System.out.println("id : "+id+" ,A : "+a);
        }
    
        public A get(){
            return a;
        }
    
        public void set(A a){
            System.out.println("执行set方法:" + a);
        }
    }
    public class FanXingDemo {
        public static void main(String[] args) {
            FanXingClass<String> fxc = new FanXingClass<String>();
            fxc.setA("mashibing");
            fxc.setId(1);
            fxc.show();
    
            FanXingClass<Integer> fxc2 = new FanXingClass<Integer>();
            fxc2.setA(34);
            fxc2.setId(2);
            fxc2.show();
    
            FanXingClass<Person> fxc3 = new FanXingClass<Person>();
            fxc3.setA(new Person("aaa",123));
            fxc3.setId(3);
            fxc3.show();
            System.out.println(fxc3.get());
            fxc3.set(new Person("hhe",123));
        }
    }

    打印结果为:

    /*
    id : 1 ,A : mashibing
    id : 2 ,A : 34
    id : 3 ,A : Person{name='aaa', age=123}
    Person{name='aaa', age=123}
    执行set方法:Person{name='hhe', age=123}
    
    Process finished with exit code 0
    */

    2、泛型接口

    /*
     *      2、泛型接口
     *          在定义接口的时候,在接口的名称后添加<E,K,V,A,B>,
     *          1、子类在进行实现的时候,可以不填写泛型的类型,此时在创建具体的子类对象的时候才决定使用什么类型
     *          2、子类在实现泛型接口的时候,只在实现父类的接口的时候指定父类的泛型类型即可,此时,测试方法中的泛型类型必须要跟子类保持一致
    */
    public interface FanXingInterface<B> {
    
       public B test();
    
       public void test2(B b);
    }

    怎么对它进行实现

    public  class   FanXingInterfaceSub implements FanXingInterface<String> {
    
    
        @Override
        public String test() {
            return null;
        }
    
        @Override
        public void test2(String string) {
            System.out.println(string);
        }
    }
    public class FanXingDemo {
        public static void main(String[] args) {
            FanXingInterfaceSub fxi = new FanXingInterfaceSub() ;
            fxi.test2("123");
        }
    }

    打印结果为:

    /*
    123
    
    Process finished with exit code 0
    */

    return null 这里的返回值应该如何写, FanXingInterfaceSub<String> 不是真正的String

    FanXingInterfaceSub<Integer> fxm = new FanXingInterface<Ingeger>();

    也可以

    3、泛型方法

    /*
    *          在定义方法的时候,指定方法的返回值和参数是自定义的占位符,可以是类名中的T,也可以是自定义的Q,只不过在使用Q的时候需要使用
     *          <Q>定义在返回值的前面
     */
    public class FanXingMethod<T> {
    
        private T t;
    
        public T getT() {
            return t;
        }
    
        public void setT(T t) {
            this.t = t;
        }
    
        public <Q> void show(Q q){
            System.out.println(q);
            System.out.println(t);
        }
    }
    public class FanXingDemo {
        public static void main(String[] args) {
            FanXingMethod<String> fxm = new FanXingMethod<>();
            fxm.setT("ttt");
            fxm.show(123);
            fxm.show(true);
        }
    }

    打印结果为:

    /*
    123
    ttt
    true
    ttt
    
    Process finished with exit code 0
    */

    4、泛型的上限(工作中不用)

    extends 继承

    如果父类确定了,所有的子类都可以直接使用

    5、泛型的下限(工作中不用)

     

    如果子类确定了,子类的所有父类都可以直接传递参数使用

     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    第五周
    第三章 程序的机器级表示
    第二章 信息的表示和处理
    嵌入式Linux应用开发——Linux下的C编程基础
    Linux基础入门(20135207 王国伊)
    Java实验报告(实验四)
    linux系统之pam模块
    linux 从入门到跑路-时间,日期问题
    linux 从入门到跑路-Shell和部分命令
    linux 从入门到跑路-挂载,命令的执行顺序
  • 原文地址:https://www.cnblogs.com/kongxiangqun/p/16164373.html
Copyright © 2020-2023  润新知