十七.集合(单例集合Collection之Set)Object类 比较器

6.Object类

Object类是Java中所有类的基类

构造方法:

Object( ) 可以直接实例化

学习三个方法:

  1. String toString( ) 返回对象的字符串表示形式

​ 一般来说返回的应该是一个简明扼要的表达,建议所有子类覆盖该方法,在object对象中,tostring方法返回一个 类名@该对象哈希码(内存地址)的无符号的十六进制表达形式的字符串

getClass().getName() + '@' + Integer.toHexString(hashCode()) 

需要的结果:

对象的字符串表达形式 所以要重写方法

package com.qfedu.a_object;
class Person {
    String name;
    int age;
    //person类是Object类子类不? 是!!!


    //
    @Override
    public String toString() {
        System.out.println("123");
        return
                "name=\"" + name + '\"' +
                ", age=" + age
                ;
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Object obj = new Object();
        Person person = new Person();
        System.out.println(person);

    }
}
  1. boolean equals( Object obj ) 指示对象是否相等
public boolean equals(Object obj) {
     return (this == obj);
 }

Object类下的equals方法比较的是两个对象的地址值是否相等,String类下的eequals比较的是字符串的内容,因为String类重写了Object类下的equals方法

为什么重写???

父类的需求满足不了子类的需求

比较两个对象的内容是否一样,一样返回true,否则返回false

package com.qfedu.a_object;

import java.util.Objects;

class Student {
    String name;
    int age;

    public Student(String name, int age) {

        this.name = name;
        this.age = age;
    }
    //重写equlas,要求去比较内容,如果内容一样的额话,返回true


    //stu1.equals(stu2)
    //stu2赋值给了 o  向上转型 Object o =new Student();
    @Override
    public boolean equals(Object o) {
        if (this == o) {//比较是地址
            return true;
        }
        //如果地址不一样的话,再去比较内容,如果内容一样也返回true
        if (o instanceof Student) {
            //才去比较值 name  age
            Student stu = (Student)o;//向下转型
            //stu1.equals(stu2)  stu就是stu2  this 是stu1
            return stu.age == this.age && stu.name.equals(this.name);

        }
        return false;
    }


}
public class Demo2 {
    public static void main(String[] args) {
        Student stu1 = new Student("老邢", 89);
        Student stu2 = new Student("老邢", 89);
        //stu1是Object类子类,用的是object 类面的equals方法
        //Object类下面的equals方法比较是 地址  this==obj
        //System.out.println(stu1.equals(stu2));//false
        //现在我的需求是当两个对象的内容一致的时候返回的额是true
        //内容不一样的时候,返回是false
        //就意味着Object类的equals方法已经满足不了Student类的需求了
        //咋解决? 重写equals方法
        System.out.println(stu1.equals(stu2));//true
    }
}

  1. int hashCode ( ) 哈希码值,在Object类下将内存地址(十六进制值)转为十进制的值

返回对象的哈希码值,支持该方法是为了散列表,如HashMap

注意事项:

如果根据equals(Object)方法两个对象相等,则在两个对象中的每个对象上调用hashCode方法必须产生相同的整数结果。

如果两个对象的hashcode相等,这两个对象不一定相等

由类别Object定义的hashCode方法确实为不同对象返回不同的整数。 (hash码的值通常通过将对象的内部地址转换为整数来实现,但Java的编程语言不需要此实现技术。)

为什么要重写hashCode( ) 方法???

重写了equals方法后可能会导致两个不同地址值的对象内容一样,所以他们相等,根据hashCode规范,当两个对象相等时,h哈希值也要相等,所以要重写hashcode使其满足规范

String类就重写了hashCode( ) ,不同地址的对象内容一样,哈希值相同

无论何时重写equals方法,通常需要重写hashCode方法,以便维护hashCode方法的通用合同,该方法规定相等的对象必须具有相等的哈希码

package com.qfedu.a_object;

import java.util.Objects;

class Dog {
    int id;
    String name;

    public Dog(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public boolean equals (Object o) {
        if (this ==  o) {
            return  true;
        }
        if (o instanceof Dog) {
            Dog dog = (Dog)o;
            return this.id == dog.id && dog.name.equals(this.name);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return name.hashCode() + id;
    }
}
public class Demo5 {
    public static void main(String[] args) {
        Dog dog1 = new Dog( 3, "a");
        Dog dog2 = new Dog( 2, "b");
        //现在关注的是内容,如果内容一样 调用equals方法的时候
        //必须返回一个true
        System.out.println(dog1.equals(dog2));//true
        //此时这个两个对象的hash值一样不一样?因为内存地址不一样的
        System.out.println(dog1.hashCode());
        System.out.println(dog2.hashCode());
        //现在hashCode不一样咋办? 重写hashCode即可
//如果根据equals(Object)方法两个对象相等,
// 则在两个对象中的每个对象上调用hashCode方法必须产生相同的整数结果。
        //如果两个对象的hash值一样,对象是不一定一样的。但是如果两个对象相等
        //那么hash值一定相等
    }
}

7.Set集合接口

set集合也是一个子接口,父接口是Collection。存储的数据时无序,不重复的

有两个实现类:

HashSet:底层依靠hash值记性存储,两个元素hash值一样就不 在存储

TreeSet:底层是二叉树,对存储的数据进行自然排序

7.1hashSet类
package com.qfedu.b_hashSet;

import java.util.HashSet;
import java.util.Set;

public class Demo1 {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        //set集合存储的无序的  不可重复的
        set.add("b");
        set.add("a");
        set.add("d");
        set.add("c");
        set.add("a");
        set.add("ad");
        System.out.println(set);

        Set<Integer> set1 = new HashSet<>();
        set1.add(78);
        set1.add(23);
        set1.add(100);
        set1.add(56);
        System.out.println(set1);
        set1.remove(23);
        //循环
        for (Integer integer : set1) {
            System.out.println(integer);
        }

    }
}
7.2hashSet集合存对象
package com.qfedu.b_hashSet;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

class Person {
    int id;
    String name;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id == person.id && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
public class Demo2 {
    public static void main(String[] args) {
        Person person1 = new Person(1, "zhangsan");
        Person person2 = new Person(1, "李四");
        Person person3 = new Person(1, "李四");
        Person person4 = new Person(1, "李四");
        Set<Person> set = new HashSet<>();
        //在调用add方法的时候 底层在调用hashCode方法和equals
        set.add(person1);
        set.add(person2);
        set.add(person3);
        set.add(person4);
        System.out.println(set);
        //感觉不太合适,发现存的两个对象的内容是一样。真实开发的时候
        //只关注内容的,如果内容一样,我也让你存不进去。!!!
        //咋办

        //总结:以后set集合中如果想存对象的时候,要求对象的内容如果一样的
        //话,不能存到set集合中,咋办?重写equals方法和hahsCode方法


        //hash值不一样,对象一定不一样。
        //对象一样的话,hash只能一定一样
    }
}
7.3TreeSet集合

同样实现了set集合,可以保证数据唯一性,存储也是无序的

要比较大小,会自动排序

package com.qfedu.c_treeSet;

import java.util.Set;
import java.util.TreeSet;

public class Demo1 {
    public static void main(String[] args) {
        //TreeSet在存储的数据的时候 会排序
        Set<Integer> set = new TreeSet<>();
        set.add(89);
        set.add(79);
        set.add(69);
        set.add(109);
        set.add(39);
        System.out.println(set);

        Set<String> set1 = new TreeSet<>();
        set1.add("d");
        set1.add("w");
        set1.add("a");
        set1.add("c");
        System.out.println(set1);
    }
}
7.4TreeSet集合中存对象

如果要存的是对象的话,也要能够比较大小,所以要实现comparableTo接口

intcompareTo(T o)将此对象与指定的对象进行比较以进行排序。

将对象和指定对象进行比较,返回一个负整数,零或正整数

需要用对象中的int类数据

package com.qfedu.c_treeSet;

import java.util.Set;
import java.util.TreeSet;

class Student implements Comparable<Student>{
    String name;
    int age;

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

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

    @Override
    public int compareTo(Student o) {
        System.out.println("123");
        int num = this.age - o.age;
        return num;
    }
}
public class Demo2 {
    public static void main(String[] args) {
        Student stu1 = new Student("老邢", 45);
        Student stu2 = new Student("老邢", 35);
        Student stu3 = new Student("saolei", 25);
        Student stu4 = new Student("老万", 87);
        //按照年龄进行排序 存到TreeSet集合中
        Set<Student> set = new TreeSet<>();
        set.add(stu1);
        set.add(stu2);
        set.add(stu3);
        set.add(stu4);
        System.out.println(set);

        //  Exception in thread "main" java.lang.ClassCastException:
        //  com.qfedu.c_treeSet.Student cannot be cast to java.lang.Comparable
        //	at java.util.TreeMap.compare(TreeMap.java:1294)
        //类转换异常的错误,Student转换不了Comparable
        //y因为底层在进行排序的时候,实现了Comparable这个接口
        //为啥泛型String没有报错  Integer也没有报错
    }
}

数据结构二叉树的简单介绍

通过查阅API我们得知TreeSet集合是基于TreeMap的实现,而TreeMap是基于二叉树(红黑树)结构,也就是说TreeSet集合的底层使用的二叉树(红黑树)结构。

树结构:它也是数据结构中的一种。在计算机领域中树结构指的是倒立的树。

树结构存储的数据,每个数据也需要节点来保存。

而TreeSet集合底层是二叉树的数据结构,什么是二叉树呢?

二叉树:每个节点的下面最多只能有2个子节点。

说明:最多表示一个节点下面可以有两个子节点或者一个子节点或者没有子节点。

在二叉树的根节点左侧的节点称为左子树,在根节点的右侧的节点称为右子树。

既然已经得知TreeSet集合底层是二叉树,那么二叉树是怎样存储数据的呢?是怎样保证存储的数据唯一并有序的呢?

二叉树的存储流程:

当存储一个元素的时候,如果是树的第一个元素,这个元素就作为根节点。

如果不是第一个元素,那么就拿要存储的元素与根节点进行比较大小:

大于根元素:就将要存储的元素放到根节点的右侧,作为右叶子节点。

等于根元素:丢弃。

小于根元素:就将要存储的元素放到根节点的左侧,作为左叶子节点。

总结:二叉树是通过比较大小来保证元素唯一和排序的。
20  10  31  5  13  23 51
http://data.biancheng.net/view/192.html
> TreeSet案例
>
> 使用TreeSet存储Employee对象,比较两个属性
>
> ​		int age,  int weight   先按照年龄进行升序排,如果年龄相等的话,按照体重升序排

```java
package com.qfedu.a_treeset;

import java.util.Set;
import java.util.TreeSet;

class Employee implements Comparable<Employee>{
    String name;
    int age;
    int weight;

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

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

    @Override
    public int compareTo(Employee o) {
        //先按照年两比,如果年龄相等 就比较体重
        int num = this.age - o.age;
        if (num == 0) {
            int num1 = o.weight - this.weight;
            return num1;
        }
        return num;
    }
}
public class Demo2 {
    public static void main(String[] args) {
        Set<Employee> set = new TreeSet<>();
        set.add(new Employee("广坤", 35, 78));
        set.add(new Employee("二贝", 26, 70));
        set.add(new Employee("赵四", 35, 72));
        set.add(new Employee("彩云", 35, 79));
        set.add(new Employee("鸡哥", 32, 59));
        set.add(new Employee("正经博", 32, 59));
        System.out.println(set);
    }
}

TreeSet里面存的是Dog类,

​ 两个属性: String name, int age

​ 先按照字符串的字典顺序排,然后字符串相等的话,在按照年龄排

package com.qfedu.a_treeset;

import java.util.Set;
import java.util.TreeSet;

class Dog implements Comparable<Dog>{
    String name;
    int age;

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

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

    @Override
    public int compareTo(Dog o) {
        //先按照字典的顺序进行排,如果字符串相等再按照年龄升序排
        int num = this.name.compareTo(o.name);
        if (num == 0) {
            //字符串相等的情况,又要比较年领
            int num1 = this.age - o.age;
            return num1;

        }
        return num;
    }
}
public class Demo3 {
    public static void main(String[] args) {
        Set<Dog> set = new TreeSet<>();
        set.add(new Dog("彩云", 5));
        set.add(new Dog("旺财", 2));
        set.add(new Dog("大黄", 6));
        set.add(new Dog("大黄", 3));
        set.add(new Dog("大黄", 4));
        System.out.println(set);
    }
}

总结:使用TreeSet时需要类实现一个接口Comparable这个接口去做比较排序,重写comparableTo()方法

还有一种比较器的写法Comparator接口

7.5使用比较器将数据存到TreeSet中
package com.qfedu.b_comparator;

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

class Student {
    String name;
    int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
class MyComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        int num = o1.age - o2.age;
        return num;
    }
}
public class Demo1 {
    public static void main(String[] args) {
        //如果想要使用比较器的写法 必须再new TreeSet的时候
        //加上比较器对象
        //TreeSet 有一个有参构造,有参构造的方法是Comparwator的对象
        //Comparator是一个接口 不能实例化,咋办?再写一个类去实现这个接口
        Set<Student> students = new TreeSet<>(new MyComparator());
        students.add(new Student("维一", 23));
        students.add(new Student("永康", 19));
        students.add(new Student("赵娜", 18));
        students.add(new Student("运铎", 28));
        students.add(new Student("佳祥", 36));
        System.out.println(students);
    }
}

总结

ArrayList:单纯的使用add

LinkList:单纯add

HashSet:不单纯,要重写equals和hashCode方法

TreeSet:不单纯,要在类中实现Comparable接口,给类添加比 较排序功能

开发中使用ArrayList



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值