33Set集合

1.1 Set集合概述和特点

  • Set集合的特点
    • 元素存取无序
    • 没有索引、只能通过迭代器或增强for循环遍历
    • 不能存储重复元素

1.2 哈希值【理解】

  • 哈希值简介

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

  • 如何获取哈希值
    Object类中的public int hashCode():返回对象的哈希码值

  • 哈希值的特点

    • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
    • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同

1.3 HashSet集合概述和特点【应用】

  • HashSet集合的特点
    • 底层数据结构是哈希表
    • 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
    • 没有带索引的方法,所以不能使用普通for循环遍历
    • 由于是Set集合,所以是不包含重复元素的集合

1.4 HashSet集合保证元素唯一性源码分析【理解】

Snipaste_2021-09-20_17-33-18

源码分析:

//创建集合对象
HashSet<String> hs = new HashSet<String>();

//添加元素
hs.add("hello");
hs.add("world");
hs.add("java");
----------------------------------------------

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

//hash值和元素的hashCode()方法相关
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;

    //如果哈希表未初始化,就对其进行初始化
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;

    //根据对象的哈希值计算对象的存储位置,如果该位置没有元素,就存储元素
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        /*
            存入的元素和以前的元素比较哈希值
                如果哈希值不同,会继续向下执行,把元素添加到集合
                如果哈希值相同,会调用对象的equals()方法比较
                    如果返回false,会继续向下执行,把元素添加到集合
                    如果返回true,说明元素重复,不存储
        */
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        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.5 常见数据结构之哈希表【理解】

Snipaste_2021-09-20_17-37-17

案例:HashSet集合存储学生对象并遍历【应用】

  • Student类
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    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 boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

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

  • 测试
import java.util.HashSet;

public class test {
    public static void main(String[] args) {
        //创建HashSet集合对象
        HashSet<Student> hs = new HashSet<Student>();

        //创建学生对象

        Student s4 = new Student("A", 33);
        Student s5 = new Student("B",21);
        Student s6  = new Student("B",21);

        //把学生添加到集合
        hs.add(s4);
        hs.add(s5);
        hs.add(s6);

        //遍历集合(增强for)
        for (Student s : hs) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

快速重写equals()和hashCode():

Alt+Insert找到equals()&hashCode(),选择IntelliJ Default

1.6 LinkedHashSet集合概述和特点【应用】

  • LinkedHashSet集合特点
    • 哈希表和链表实现的Set接口,具有可预测的迭代次序
    • 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
    • 由哈希表保证元素唯一,也就是说没有重复的元素
import java.util.LinkedHashSet;

/*
    LinkedHashSet集合特点
        1:哈希表和链表实现的Set接口,具有可预测的迭代次序
        2:由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
        3:由哈希表保证元素唯一,也就是说没有重复的元素
 */
public class LinkedHashSetDemo {
    public static void main(String[] args) {
        //创建集合对象
        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>();

        //添加元素
        linkedHashSet.add("hello");
        linkedHashSet.add("world");
        linkedHashSet.add("java");

        linkedHashSet.add("world");

        //遍历集合
        for(String s : linkedHashSet) {
            System.out.println(s);
        }
    }
}

2. Set集合排序

2.1 TreeSet集合概述和特点【应用】

  • TreeSet集合概述

    • 元素有序,可以按照一定的规则进行排序,具体排序方式取决于构造方法

      • TreeSet():根据其元素的自然排序进行排序
      • TreeSet(Comparator comparator) :根据指定的比较器进行排序
    • 没有带索引的方法,所以不能使用普通for循环遍历

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

import java.util.TreeSet;

/*
    TreeSet集合特点
        1:元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
            TreeSet():根据其元素的自然排序进行排序
            TreeSet(Comparator comparator) :根据指定的比较器进行排序
        2:没有带索引的方法,所以不能使用普通for循环遍历
        3:由于是Set集合,所以不包含重复元素的集合
 */
public class TreeSetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Integer> ts = new TreeSet<Integer>();

        //添加元素
        ts.add(10);
        ts.add(40);
        ts.add(30);
        ts.add(50);
        ts.add(20);

        ts.add(30);

        //遍历集合
        for(Integer i : ts) {
            System.out.println(i);
        }
    }
}

2.2 自然排序Comparable的使用【应用】

  • 案例需求

    • 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
    • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
  • 实现步骤

    • 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
    • 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
    • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写

学生类:

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

    public Student() {
    }

    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 int compareTo(Student s) {
        //返回0认为重复不添加,返回1按存储顺序升序,返回-1按存储顺序降序
//        return 0;
//        return 1;

        //按照年龄升序排列
  /**    执行第一个add时,this.age和s.age都是指向29(s1)
  		 执行第二个add是,this.age指向28(s2),s.age指向29(s1)*/
//        int a=this.age;
//        int b =s.age;
        int num = this.age-s.age;
//        int num s.age-this.age;//降序

        //按照年龄,姓名排序
        int num2 = num == 0 ? this.name.compareTo(s.name) : num;
        return num2;


    }
}

测试类:

import java.util.TreeSet;

public class Demo {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>();

        //创建学生对象
        Student s1 = new Student("A", 29);
        Student s2 = new Student("B", 28);
        Student s3 = new Student("C", 30);
        Student s4 = new Student("D", 33);

        Student s5 = new Student("A",29);
        Student s6 = new Student("E",33);

        //把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历集合
        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

1.3 比较器排序Comparator的使用【应用】

  • 案例需求

    • 存储学生对象并遍历,创建TreeSet集合使用带参构造方法
    • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
  • 实现步骤

    • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
    • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
    • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    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;
    }
}

测试:

import java.util.Comparator;import java.util.TreeSet;public class Demo {    public static void main(String[] args) {        //创建集合对象        TreeSet<Student> ts =new TreeSet<Student>(new Comparator<Student>() {            @Override            public int compare(Student s1, Student s2) {                //this.age - s.age                //s1,s2                int num = s1.getAge() - s2.getAge();                int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;                return num2;            }        });        //创建学生对象        Student s1 = new Student("A", 29);        Student s2 = new Student("B", 28);        Student s3 = new Student("C", 30);        Student s4 = new Student("D", 33);        Student s5 = new Student("A",29);        Student s6 = new Student("E",33);        //把学生添加到集合        ts.add(s1);        ts.add(s2);        ts.add(s3);        ts.add(s4);        ts.add(s5);        ts.add(s6);        //遍历集合        for (Student s : ts) {            System.out.println(s.getName() + "," + s.getAge());        }    }}

Snipaste_2021-09-24_12-36-14

案例:成绩排序案例

  • 案例需求
    • 用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合
    • 要求:按照总分从高到低出现

学生类:

public class Student {    private String name;    private int cg;    private int mg;    public Student() {    }    public Student(String name, int cg, int mg) {        this.name = name;        this.cg = cg;        this.mg = mg;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getCg() {        return cg;    }    public void setCg(int cg) {        this.cg = cg;    }    public int getMg() {        return mg;    }    public void setMg(int mg) {        this.mg = mg;    }    public int getSum(){        return (this.cg+this.mg);    }}

测试:

/*
    需求:
        用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合
        要求:按照总分从高到低出现

    思路:
        1:定义学生类
        2:创建TreeSet集合对象,通过比较器排序进行排序
        3:创建学生对象
        4:把学生对象添加到集合
        5:遍历集合
 */

public class test {
    public static void main(String[] args) {
        TreeSet<Student> st = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {

//                return 0;

                //主要条件
                int num = s2.getSum() - s1.getSum();
                //次要条件
                int num2 = num == 0 ? s1.getCg() - s2.getCg() : num;
                int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) : num2;
                return num3;
            }
        });

        //创建学生对象
        Student s1 = new Student("q", 98, 100);
        Student s2 = new Student("w", 95, 95);
        Student s3 = new Student("e", 100, 93);
        Student s4 = new Student("r", 100, 97);
        Student s5 = new Student("t", 98, 98);

        Student s6 = new Student("p", 97, 99);
        Student s7 = new Student("p", 97, 99);

        //把学生对象添加到集合
        st.add(s1);
        st.add(s2);
        st.add(s3);
        st.add(s4);
        st.add(s5);
        st.add(s6);
        st.add(s7);

        //遍历集合
        for (Student s : st) {
            System.out.println(s.getName() + "," + s.getCg() + "," + s.getMg() + "," + s.getSum());
        }
    }
}

案例:不重复的随机数案例

  • 案例需求
    • 编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出

Snipaste_2021-09-24_12-36-14

*
    需求:
        编写一个程序,获取101-20之间的随机数,要求随机数不能重复,并在控制台输出

    思路:
        1:创建Set集合对象
        2:创建随机数对象
        3:判断集合的长度是不是小于10
            是:产生一个随机数,添加到集合
            回到3继续
        4:遍历集合
 */
public class SetDemo {
    public static void main(String[] args) {
        //创建Set集合对象
//        Set<Integer> set = new HashSet<Integer>();
        Set<Integer> set = new TreeSet<Integer>();

        //创建随机数对象
        Random r = new Random();

        //判断集合的长度是不是小于10
        while (set.size()<10) {
            //产生一个随机数,添加到集合
            int number = r.nextInt(20) + 1;
            set.add(number);
        }

        //遍历集合
        for(Integer i : set) {
            System.out.println(i);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值