JAVA入门之数据结构(Set集合)笔记(36)

        tab[i] = newNode(hash, key, value, null);

    else {

        Node<K,V> e; K k;

        /*

        	8、通过前面比较如果执行到这里,说明该存储位置有元素,那么就比较存入的元素和以前的元素的哈希值

        		这是一个短路运算符,如果前面为false,那么后面不会执行

        		8.1、如果这两个哈希值不同,那么返回false,if不成立,继续向下执行,把元素添加到集合

        		8.2、如果哈希值相同,那么会执行(k = p.key) == key || (key != null && key.equals(k)))

        			首先进行对象的比较(比较地址),如果两个地址值相同,那么说明这两个是同一个元素。

        			如果地址值不相同,那就调用equal方法,判断这两个元素的内容是否一样(两个元素哈希值一样),如果一样则						不存入集合,如果不一样,那么则把元素添加到集合中

        */

        if (p.hash == hash &&

            ((k = p.key) == key || (key != null && key.equals(k))))

            e = p;

        //这个else if先不用看

        else if (p instanceof TreeNode)

            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

        else {

            for (int binCount = 0; ; ++binCount) {

                if ((e = p.next) == null) {

                    p.next = newNode(hash, key, value, null);

                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st

                        treeifyBin(tab, hash);

                    break;

                }

                if (e.hash == hash &&

                    ((k = e.key) == key || (key != null && key.equals(k))))

                    break;

                p = e;

            }

        }

        if (e != null) { // existing mapping for key

            V oldValue = e.value;

            if (!onlyIfAbsent || oldValue == null)

                e.value = value;

            afterNodeAccess(e);

            return oldValue;

        }

    }

    ++modCount;

    if (++size > threshold)

        resize();

    afterNodeInsertion(evict);

    return null;

}



简单概括来说就是以下几步



1.  先如果两个元素存储位置不相同,那么直接把元素添加到集合即可

2.  如果两个元素存储位置相同,那么比较两个元素的哈希值

3.  如果两个元素的哈希值不一样,那么存储进去

4.  如果两个元素的哈希值一样,那么比较两个元素的内容

5.  如果两个元素的内容一样,那么不添加进集合

6.  如果两个元素的内容不一样,那么不用添加进集合



HashSet集合之所以可以一个位置存储多个元素,那是因为HashSet集合整体是数组结构,而每个位置上是链式结构,所以一个位置能存储多个元素



* * *



[]( )四、哈希值

------------------------------------------------------------------------



哈希值:是JDK根据**对象地址**或者**字符串**或者**数字**算出来的int类型的数值



因此在Object类中有一个方法可以获取**对象的哈希值**



*   public int hashCode() :返回对象的哈希码值



哈希值的特点在代码中体现



package CodeDemo;

/*

学生对象类

*/

public class Student {

private int age;

private String name;



public  Student(){}



public Student(int age, String name) {

    this.age = age;

    this.name = name;

}



public int getAge() {

    return age;

}



public void setAge(int age) {

    this.age = age;

}



public String getName() {

    return name;

}



public void setName(String name) {

    this.name = name;

}

}


package CodeDemo;

public class CodeDemo {

public static void main(String[] args) {

    Student s1 = new Student(18, "刘德华");

    Student s2 = new Student(18, "刘德华");

    Student s3 = new Student(20, "周星驰");



    //同一对象多次调用hashCode()方法返回的哈希值是相同的

    System.out.println(s1.hashCode());

    System.out.println(s1.hashCode());

    System.out.println("----------------------");



    //默认情况下,不同对象的哈希值是不同的

    //可以通过方法重写,可以实现不同对象的哈希值相同

    System.out.println(s2.hashCode());

    System.out.println(s3.hashCode());

    System.out.println("----------------------");



    //不同的字符串对应的哈希值也是不同的

    System.out.println("Hello".hashCode());

    System.out.println("World".hashCode());

    System.out.println("----------------------");



    //但相同的字符串对应的哈希值是相同的

    System.out.println("JAVA".hashCode());

    System.out.println("JAVA".hashCode());

    System.out.println("----------------------");



    //不同汉字的哈希值可能一样

    System.out.println("重地".hashCode());

    System.out.println("通话".hashCode());

    System.out.println("中秋".hashCode());

}

}




运行结果



149928006

149928006


713338599

168423058


69609650

83766130


2269730

2269730


1179395

1179395

651582




* * *



[]( )五、HashSet学生实例应用

----------------------------------------------------------------------------------



> 需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序遍历

> 

> 要求:学生对象的成员变量值相同,认为是同一个对象



**思路**



1.  定义学生类

2.  创建HashSet集合对象

3.  创建学生对象

4.  把学生添加到集合

5.  遍历集合(增强for)

6.  在学生类中重写两个方法(hashCode()和equals())



**代码实现**



package CodeDemo;

/*

学生类

*/

import java.util.Objects;

public class Student {

private int age;

private String name;



public  Student(){}



public Student(int age, String name) {

    this.age = age;

    this.name = name;

}



public int getAge() {

    return age;

}



public void setAge(int age) {

    this.age = age;

}



public String getName() {

    return name;

}



public void setName(String name) {

    this.name = name;

}



@Override

public String toString() {

    return "Student{" +

            "age=" + age +

            ", name='" + name + '\'' +

            '}';

}



@Override

public boolean equals(Object o) {

    if (this == o) return true;

    if (o == null || getClass() != o.getClass()) return false;

    Student student = (Student) o;

    return age == student.age &&

            Objects.equals(name, student.name);

}



@Override

public int hashCode() {

    return Objects.hash(age, name);

}

}


package CodeDemo;

/*

测试类

*/

import java.util.HashSet;

import java.util.Iterator;

public class HashDemo {

public static void main(String[] args) {

    Student s1 = new Student(19, "成龙");

    Student s2 = new Student(20, "刘德华");

    Student s3 = new Student(20, "刘德华");



    HashSet<Student> hashSet = new HashSet<Student>();



    hashSet.add(s1);

    hashSet.add(s2);

    hashSet.add(s1);

    hashSet.add(s3);



    for (Student s : hashSet){

        System.out.println(s);

    }



    Iterator<Student> iterator = hashSet.iterator();

    while (iterator.hasNext()){

        Student s = iterator.next();

        System.out.println(s);

    }

}

}




**运行结果**



Student{age=19, name=‘成龙’}

Student{age=20, name=‘刘德华’}

Student{age=19, name=‘成龙’}

Student{age=20, name=‘刘德华’}




为什么要重写hashCode方法呢???



那是因为hashcode重写的好还可以提升hashset的效率



[]( )六、LinkedHashSet集合概述和特点

-----------------------------------------------------------------------------------------



特点



1.  哈希表和链式实现的Set接口,具有可预测的迭代次序

2.  由链式保证元素有序,也就是说元素的存储和取出顺序是一致的

3.  由哈希表保证元素唯一,也就是说没有重复的元素



package CodeDemo;

import java.util.LinkedHashSet;

public class LinkDemo {

public static void main(String[] args) {

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

    linkedHashSet.add("Hello");

    linkedHashSet.add("World");

    linkedHashSet.add("java");

    linkedHashSet.add("Hello");



    for (String s : linkedHashSet){

        System.out.println(s);

    }

}

}


Hello

World

java




[]( )七、TreeSet集合概述和特点

-----------------------------------------------------------------------------------



TreeSet集合特点



> TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序排列



1.  元素有序,但是这里的顺序值得不是存储和取出顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法

    

    TreeSet():根据其元素的自然排序进行排序(自然排序是指ABCD这样的顺序)

    

    TreeSet(Comparator comparator):根据指定的比较器进行排序

    

2.  没有带索引的方法,所以不能使用普通的for循环遍历

    

3.  由于是Set集合,所以不包含重复元素的集合

    



package CodeDemo;

import java.util.TreeSet;

public class TreeDemo {

public static void main(String[] args) {

    //创建集合对象

    //集合存储的是对象(引用类型),所以存储整数应该存储Inerger

    TreeSet<Integer> tree = new TreeSet<Integer>();



    //添加数据

    tree.add(10);

    tree.add(100);

    tree.add(1000);

    tree.add(10);

    tree.add(1);



    for (int num : tree){

        System.out.println(num);

    }

}

}




输出结果



1

10

100

1000




由结果可见,输出的数据没有重复的数据



并且输入的顺序是10、100、1000、1,但是输出的顺序是1、10、100、1000,所以可证明TreeSet( )根据其元素的自然排序进行排序.



[]( )八、自然排序Comparable的使用

--------------------------------------------------------------------------------------



从例子中体会其作用



*   存储学生对象并遍历,创建TreeSet集合使用无参构造方法

*   要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序



package CodeDemo;

/*

学生类

*/

import java.util.Objects;

public class Student implements Comparable {

private int age;

private String name;



public  Student(){}



public Student(int age, String name) {

    this.age = age;

    this.name = name;

}



public int getAge() {

    return age;

}



public void setAge(int age) {

    this.age = age;

}



public String getName() {

    return name;

}



public void setName(String name) {

    this.name = name;

}



@Override

public String toString() {

    return "Student{" +

            "age=" + age +

            ", name='" + name + '\'' +

            '}';

}



@Override

public boolean equals(Object o) {

    if (this == o) return true;

    if (o == null || getClass() != o.getClass()) return false;

    Student student = (Student) o;

    return age == student.age &&

            Objects.equals(name, student.name);

}



@Override

public int hashCode() {

    return Objects.hash(age, name);

}



@Override

public int compareTo(Student o) {

    //return 0; 表示相同

    //return 1; 表示从小到大排序

    //return -1; 表示从大到小排序

    //按照年龄从小到大排序

    int num = this.age - o.age;

    //年龄相同时候,按照姓名字母顺序排序

    int num2 = num == 0? this.name.compareTo(o.name) : num;

    return num2;

}

}


package CodeDemo;

import java.util.TreeSet;

public class ComparableDemo {

public static void main(String[] args) {

    //创建集合对象

    TreeSet<Student> treeSet = new TreeSet<Student>();



    //创建学生对象

    Student s1 = new Student(19, "小明");

    Student s2 = new Student(20, "小红");

    Student s3 = new Student(20, "小白");



    //添加到集合中

    treeSet.add(s1);

    treeSet.add(s2);

    treeSet.add(s3);

    treeSet.add(s1);



    for (Student s : treeSet){

        System.out.println(s);

    }

}

}




n’n’n



运行结果



Student{age=19, name=‘小明’}

Student{age=20, name=‘小白’}

Student{age=20, name=‘小红’}




结论



*   用TreeSet集合存储自定义对象,无参构造方法使用的是**自然排序**对元素进行排序

*   自然排序,就是**让元素所有的类实现Comparable接口**,重写compareTo(T o)方法

*   重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写



[]( )九、比较器排序Comparator

------------------------------------------------------------------------------------



| `TreeSet(Comparator<? super E> comparator)` | 构造一个新的,空的树集,根据指定的比较器进行排序。 |

| --- | --- |



_**为了方便,这里可以运用匿名内部类的知识去解决**_



package TEXT;

/*

学生类

*/

public class Student {

private String name;

private int age;



public Student(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 String toString() {

    return "Student{" +

            "name='" + name + '\'' +

            ", age=" + age +

            '}';

}

}


package TEXT;

import java.util.Comparator;

import java.util.TreeSet;

public class Demo {

public static void main(String[] args) {

    //创建集合对象

    TreeSet<Student> treeSet = new TreeSet<Student>(new Comparator<Student>() {

        @Override

        public int compare(Student o1, Student o2) {

            int num = o1.getAge() - o2.getAge();

            int num2 = num == 0? o1.getName().compareTo(o2.getName()) : num;

            return num2;

        }

    });



    //创建学生对象

    Student s1 = new Student("小明", 19);

    Student s2 = new Student("小红", 20);

    Student s3 = new Student("小白", 20);



    //添加到集合中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值