集合——Set

Set

1. 特点

  • 元素存取无序

  • 没有索引,只能通过迭代器或增强for循环遍历

  • 不能存储重复的元素

  • 创建集合对象

    • Set<String> set = new HashSet<String>();
      

2. 哈希值

2.1 概述

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

2.2 获取哈希值

  • 通过Object类中的public int hashCode():返回对象的哈希码值

2.3 特点

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
  • 默认情况下,不同对象的哈希值是不同的
  • 可以通过重写hashCode()方法,实现不同的对象的哈希值相同
  • 在极少数的情况下,变量/对象的哈希码值会重复,称为"哈希碰撞"、“哈希冲突”,例如"重地"、"通话"的哈希码值就是相同的

2.4 代码示例

public class HashDemo {
    public static void main(String[] args) {
        //创建学生对象
        Student s1 = new Student("哈哈哈", 12);

        //同一个对象多次调用hashCode()方法返回的哈希值是相同的
        System.out.println(s1.hashCode()); //356573597
        System.out.println(s1.hashCode()); //356573597

        Student s2 = new Student("哈哈哈", 12);
        //默认情况下,不同对象调用hashCode()方法返回的哈希值是不同的
        //通过方法重写,可以实现不同对象的哈希值是相同的
        System.out.println(s2.hashCode()); //1735600054

        System.out.println("hello".hashCode()); //99162322
        System.out.println("world".hashCode()); //113318802

        //哈希碰撞 哈希冲突
        System.out.println("重地".hashCode()); //1179395
        System.out.println("通话".hashCode()); //1179395
    }
}
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;
    }
}

3. HashSet集合

3.1 特点

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

3.2 代码示例

3.2.1 基本使用
import java.util.HashSet;

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

        //添加元素
        hs.add("hello");
        hs.add("world");
        hs.add("java");
        //这里添加一个重复的元素测试 set集合是否不包含重复元素
        hs.add("world");

        //利用增强for循环遍历集合
        for (String s : hs) {
            System.out.println(s);
        }
    }
}
/* 输出结果
world        从输出结果可以看出,集合的迭代顺序与存储顺序不一致
java         且不会出现重复的数据
hello
 */
3.2.2 存储学生对象并遍历<唯一性>
import java.util.HashSet;

//HashSet集合存储学生对象并遍历
/*
1.定义学生类
2.创建HashSet集合对象
3.创建学生类
4.添加学生对象
5.遍历 增强for
 */
public class Test01 {
    public static void main(String[] args) {
        //创建集合对象
        HashSet<Student> hs = new HashSet<>();

        Student s1 = new Student("哈哈哈", 12);
        Student s2 = new Student("嘻嘻嘻", 13);
        Student s3 = new Student("吼吼吼", 14);

        Student s4 = new Student("哈哈哈", 12);

        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);

        for (Student s : hs) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}
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;
    }
    //如果这里不重写equals and hashCode 就不能保证学生对象的唯一性
    //学生对象变量值相同,我们就认为是同一个对象
    //快捷生成重写代码 Alt + Insert ---> equals() and hashCode()
    @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;
    }
}

4. 哈希表

  • 本质时元素为链表的数组,默认初始容量为16

image-20210814174701374

  • 图解
    • 首先得到每个对象的哈希值,并取余
    • 取余得到的结果就是其对象的存放位置
    • "hello"的哈希值取余结果为2,所以存放至2的位置
    • "world"的哈希值取余结果也为2,在存放前先与"hello"进行比较(哈希值、内容),不相等就以链表的形式存入
    • 重复的元素不会存储

5. LinkedHashSet集合

5.1 特点

  • 哈希表和链表实现的Set接口,具有可预测的迭代次序
  • 由链表保证元素有序,即元素存储和取出的顺序是一致的
  • 由哈希表保证元素唯一,即没有重复的元素

5.2 创建集合对象

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

6 TreeSet集合

6.1 特点

  • 元素有序,可以按照一定的规则进行排序,具体排序方法取决于构造方法
    • TreeSet():无参构造,根据其元素的自然排序进行排序
    • TreeSet(Comparator comparator):带参构造,根据指定的比较器进行排序
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以不包含重复的元素

6.2 创建集合对象

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

6.3 自然排序Comparable

6.3.1 代码示例
  • 测试类
import java.util.TreeSet;

/*存储学生对象并遍历,创建集合使用无参构造
  要求:按照年龄从小到大排序
        如果年龄想用,按姓名字母顺序排序*/
public class Test01 {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<Student>();

        Student s1 = new Student("hahaha", 12);
        Student s2 = new Student("xixixi", 15);
        Student s3 = new Student("houhouhou", 13);

        Student s4 = new Student("aoligei", 15);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);

        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getAge()); //ClassCastException
        }
    }
}
  • 学生类
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) {
        // return 0;
        // return 1;  升序
        // return -1; 降序
        //按照年龄从小到大排序

        /*
        首先输入的是s1
        然后再输入s2,s2需要与s1比较
        this.age代表的就是s2,
        s.age代表的就是s1  依此类推
         */
        //int num = this.age - s.age; 谁在要在前面,就把谁写在减号左边
        int num = this.age - s.age;

        int num2 = num == 0 ? this.name.compareTo(s.name) : num;
        return num2;
    }
}

6.4 比较器排序Comparator

6.4.1 代码示例
  • 测试类
import java.util.Comparator;
import java.util.TreeSet;

public class Test01 {
    public static void main(String[] args) {
        //匿名内部类
        TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
            @Override
            //s1写在减号左边,就是升序 s2写在减号左边 就是降序
            public int compare(Student s1, Student s2) {
                int num = s1.getAge()-s2.getAge();
                int num2 = num == 0? s1.getName().compareTo(s2.getName()):num;
                return num2;
            }
        });

        Student s1 = new Student("hahaha", 12);
        Student s2 = new Student("xixixi", 15);
        Student s3 = new Student("houhouhou", 13);

        Student s4 = new Student("aoligei", 15);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);

        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}
  • 学生类
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;
    }
}

7. 案例

7.1 成绩排序

  • 测试类
import java.util.Comparator;
import java.util.TreeSet;

//成绩排序
/*
1. 定义学生类
2. 创建TreeSet集合对象,通过比较器排序进行排序
3. 创建学生对象
4. 将学生对象添加到集合
5. 遍历集合
 */
public class Test {
    public static void main(String[] args) {
        // TreeSet有参构造  比较器排序 匿名内部类
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            /*@Override  //降序
            public int compare(Student s1, Student s2) {
                int num = s2.getsum() - s1.getsum(); //判断总分排序
                //如果总分相等
                int num2 = num == 0 ? s2.getMath() - s1.getMath() : num; //判断数学成绩排序
                //如果数学成绩也相等,那么说明成绩完全相同,按照姓名字母排序
                int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) :num2;

                return num3;*/
            @Override
            public int compare(Student s1, Student s2) {
                //说明: 当s1在前面 则代表升序  当s2在前面 则代表降序
                int num = s1.getsum() - s2.getsum(); //判断总分排序
                //如果总分相等
                int num2 = num == 0 ? s1.getMath() - s2.getMath() : num; //判断数学成绩排序
                //如果数学成绩也相等,那么说明成绩完全相同,按照姓名字母排序
                int num3 = num2 == 0 ? s2.getName().compareTo(s1.getName()) :num2;

                return num3;
            }
        });


        Student s1 = new Student("张三", 96, 95);
        Student s2 = new Student("李四", 93, 95);
        Student s3 = new Student("王五", 96, 95);
        Student s4 = new Student("赵六", 96, 93);
        Student s5 = new Student("胡七", 88, 93);
        Student s6 = new Student("何八", 93, 88);



        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        System.out.println("姓名 \t数学成绩 \t语文成绩 \t总分");
        for (Student s : ts) {
            System.out.println(s.getName() + "\t\t" + s.getMath() + "\t\t\t" + s.getChinese() + "\t\t\t"  + s.getsum());
        }
    }
}
  • 学生类
public class Student {
    private String name;
    private int math;
    private int chinese;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getMath() {
        return math;
    }

    public void setMath(int math) {
        this.math = math;
    }

    public int getChinese() {
        return chinese;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public int getsum(){
        return this.chinese + this.math;
    }
}

7.2 不重复的随机数

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

//不重复的随机数
/*
1.创建Set集合对象
2.创建随机数对象
3.判断集合的长度是不是小于10
    是:产生一个随机数,添加到集合
    回到3继续
4.遍历集合
 */
public class test {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<Integer>();
        //生成一个随机数
        Random r = new Random();
        //判断集合长度是不是小于1
        while(set.size()<10){
            //nextInt 默认取值范围[0,20)
            int number = r.nextInt(20) + 1;
            set.add(number);
        }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值