1. 程式人生 > 實用技巧 >集合(Set)【3】

集合(Set)【3】

集合

1.Set 集合

1.1Set 集合概述和特點

set集合特點

  • 不包含重複元素的集合

  • 沒有帶索引的方法,所以不能使用普通for迴圈遍歷

Set集合練習

  • 儲存字串並遍歷

public class SetDemo {
    public static void main(String[] args) {
        //建立Set物件
        Set<String> set = new HashSet<>();
        //新增元素
        set.add("浪浪");
        set.add("學習");
        set.add(
"java"); ​ //遍歷Set for(String s : set){ System.out.println(s); } } }

1.2 雜湊值

雜湊值:是JDK根據物件的地址或者字串或者數字算出來的int型別的數值

Object類中有一個方法可以獲取物件的雜湊值

  • public int hashCode():返回物件的雜湊碼值

物件的雜湊值特點

  • 同一個物件多次呼叫hashCode()方法返回的雜湊值是相同的

  • 預設情況下,不同物件的雜湊值是不同的。而重寫hashCode()方法,可以實現讓不同物件的雜湊值相同

學生實體類

public class Student {
    private String name;
    private int 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; } ​ public Student() { } ​ public Student(String name, int age) { this.name = name; this.age = age; } }

測試類

public class SetDemo {
    public static void main(String[] args) {
        Student s1 = new Student("浪浪",20);
        Student s2 = new Student("王五",20);
        Student s3 = new Student("浪浪",20);
​
        System.out.println(s1.hashCode());//1163157884
        System.out.println(s2.hashCode());//1956725890
        //預設情況下,不同物件的雜湊值是不相同的
        //通過方法重寫,可以實現不同物件的雜湊值是相同的
        System.out.println(s3.hashCode());//356573597
        System.out.println("*****************");
​
        System.out.println("浪浪".hashCode());//896320
        System.out.println("學習".hashCode());//745402
        System.out.println("java".hashCode());//3254818
​
        System.out.println("浪浪".hashCode());//896320
        System.out.println("*******************");
​
​
        System.out.println("重地".hashCode());//1179395
        System.out.println("通話".hashCode());//1179395
    }
}

1.3 HashSet集合概述和特點

hashSet集合特點

  • 底層資料結構是雜湊表

  • 對集合的迭代順序不作任何保證,也就是說不保證儲存和取出的元素順序一致

  • 沒有帶索引的方法,所以不能使用普通for迴圈遍歷

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

HashSet集合練習

  • 儲存字串並遍歷

1.4 HashSet集合保證元素唯一性原始碼分析

HashSet集合新增一個元素的過程:

HashSet集合儲存元素:

  • 要保證元素的唯一性,需要重寫hashCode 和 equals()

1.4 常見資料結構之雜湊表

雜湊表

  • JDK8之前,底層採用陣列+連結串列實現,可以說是一個元素為連結串列的陣列

  • JDK8以後,在長度比較長的時候,底層實現了優化

1.5 常見資料結構之雜湊表

案例:HashSet集合儲存學生物件並遍歷

需求:建立一個儲存學生物件的集合,儲存多個學生物件,使用程式實現在控制檯遍歷該集合

要求:學生物件的成員變數值相同,我們就認為是同一個物件

思路:

① 定義學生類

② 建立HashSet集合物件

③ 建立學生物件

④ 把學生新增到集合

⑤ 遍歷集合(增強for)

⑥ 在學生實體類中重寫hashCode()和equals()方法

學生實體類

public class Student {
    private String name;
    private int 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;
    }
​
    public Student() {
    }
​
    public Student(String name, int age) {
        this.name = name;
        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;
    }
}

測試類

public class HashSetDemo {
    public static void main(String[] args) {
        //建立HashSet集合物件
        HashSet<Student> hs = new HashSet<>();
​
        //建立學生物件
        Student s1 = new Student("張三",20);
        Student s2 = new Student("李四",22);
        Student s3 = new Student("王五",21);
        
        Student s4 = new Student("王五",21);
​
        //新增到集合中
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);
​
        //遍歷增強for
        for (Student s : hs){
            System.out.println(s.getName()+","+s.getAge());
        }
    }
}

1.6 LinkedHashSet 集合概述和特點

LinkedHashSet集合特點

  • 雜湊表和連結串列實現的Set介面,就有可預測的迭代次序

  • 由連結串列保證元素有序,就就是說元素的儲存和取出順序是一致的

  • 由雜湊表保證元素唯一,也就是說沒有重複的元素

LinkedHashSet集合練習

  • 儲存字串並遍歷

測試類

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        //建立集合物件
        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
​
        //新增元素
        linkedHashSet.add("浪浪");
        linkedHashSet.add("學習");
        linkedHashSet.add("java");
​
        //由雜湊表決定了元素的唯一性
        linkedHashSet.add("java");
        //由連結串列決定了元素是有序性 元素的存入和讀取的順序是一致的
        for (String s : linkedHashSet
             ) {
            System.out.println(s);
        }
    }
}

1.7 TreeSet集合概述和特點

TreeSet集合特點

  • 元素有序,這裡的順序不是指儲存和讀取的順序,而是按照一定的規則進行排序,具體排列方式取決於構造器

    • TreeSet():根據其元素的自然排列順序進行排列

    • TreeSet(Comparator comparator):根據指定的比較器進行排序

  • 沒有帶索引的方法,所以不能使用普通的for迴圈遍歷

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

TreeSet集合練習

  • 儲存整數並遍歷

public class TreeSetDemo {
    public static void main(String[] args) {
        //建立集合物件
        TreeSet<Integer> ts = new TreeSet<>();
​
        //新增元素
        ts.add(10);
        ts.add(40);
        ts.add(35);
        ts.add(15);
        ts.add(66);
​
        ts.add(66);
​
        //遍歷集合
        for (Integer i : ts){
            System.out.println(i);
        }
    }
}

1.8 自然排序Comparable的使用

  • 儲存學生物件並遍歷,建立TreeSet集合使用無參構造方法

  • 要求:按照年齡從小到大排序,年齡相同時,按照姓名的字母順序排序

結論

  • 用TreeSet集合儲存自定義物件,無參構造方法使用的是自然排序對元素進行排序的

  • 自然排序,就是讓元素所屬的類實現Comparable介面,重寫compareTo(To)方法

  • 重寫方法時,一定要注意排序規則必須按照要求的主要條件和次要條件來寫

學生類

public class Student implements Comparable<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;
    }
​
    public Student() {
    }
​
    @Override
    public int compareTo(Student o) {
        //return 0;//只插入一條 預設為兩個物件一樣
        //return 1;//正數 升序
        //return -1;//負數降序
        //按照年齡從小到大排序
        int num = this.age - o.age;
        int num2 = num==0?name.compareTo(o.name):num;
        return num2;
    }
​
}

測試類

public class TreeSetDemo {
    public static void main(String[] args) {
        //建立集合物件
        TreeSet<Student> ts = new TreeSet<>();
​
        //建立學生物件
        Student s1 = new Student("張三",20);
        Student s2 = new Student("李四",22);
        Student s3 = new Student("王五",21);
        Student s4 = new Student("李六",25);
​
        Student s5 = new Student("歪把七",25);
        Student s6 = new Student("歪把七",25);
​
        //新增到集合中
        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.9 比較器排序Comparator的使用

  • 儲存學生物件並遍歷,建立TreeSet集合使用帶參構造方法

  • 要求:按照年齡從小到大排序,年齡相同時,按照姓名的字母順序排序

結論

  • 用TreeSet集合儲存自定義物件,帶參構造方法使用的是比較器排序對元素進行排序的

  • 比較器排序,就是讓集合構造方法接收Comparator的實現類物件,重寫compare(T o,T o)方法

  • 重寫方法時,一定要注意排序規則必須按照要求的主要條件和次要條件來寫

學生實體類

public class Student {
    private String name;
    private int 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;
    }
​
    public Student() {
    }
​
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

測試類

public class TreeSetDemo {
    public static void main(String[] args) {
        //建立集合物件
        TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
            @Override
            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("張三",20);
        Student s2 = new Student("李四",22);
        Student s3 = new Student("王五",21);
        Student s4 = new Student("李六",25);
​
        Student s5 = new Student("歪把七",25);
        Student s6 = new Student("歪把七",25);
​
        //新增到集合中
        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());
        }
​
​
    }
}

案例: 成績排序

需求:用TreeSet集合儲存多個學生資訊(姓名、語文成績、數學成績),並遍歷該集合

要求:按照總分從高到底出現

思路:

① 定義學生類

② 建立TreeSet集合物件,通過比較器排序進行排序

③ 建立學生物件

④把學生物件新增到集合

⑤ 遍歷集合

學生實體類

public class Student {
    private String name;
    private int chinese;
    private int math;
​
    public Student() {
    }
​
    public Student(String name, int chinese, int math) {
        this.name = name;
        this.chinese = chinese;
        this.math = math;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getChinese() {
        return chinese;
    }
​
    public void setChinese(int chinese) {
        this.chinese = chinese;
    }
​
    public int getMath() {
        return math;
    }
​
    public void setMath(int math) {
        this.math = math;
    }
    public int getSum(){
        return this.chinese + this.math;
    }
}

測試類

public class TreeSetDemo {
    public static void main(String[] args) {
        //建立TreeSet集合物件,通過比較器排序進行排序
        TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                //主要條件
                int num =s1.getSum() - s2.getSum();
                //次要條件
                int num2 = num == 0 ? s1.getChinese()-s2.getChinese():num;
                int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()):num2;
                return num3;
            }
        });
​
        //建立學生物件
        Student s1 = new Student("張三",98,87);
        Student s2 = new Student("李四",94,92);
        Student s3 = new Student("王五",77,95);
        Student s4 = new Student("李六",90,83);
        Student s5 = new Student("麻子七",70,99);
​
        Student s6 = new Student("崴腳八",75,94);
​
        //新增到陣列
        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.getChinese()+","+s.getMath()+","+s.getSum());
        }
    }
}

案例: 不重複的隨機數

需求:編寫一個程式,獲取10個1-20之間的隨機數,要求隨機數不能重複,並在控制檯輸出

思路:

① 建立Set集合物件

② 建立隨機數物件

③ 判斷集合的長度是不是小於10

是:產生一個隨機數,新增到集合

回到③繼續

④ 遍歷集合

測試類

public class SetDemo {
    public static void main(String[] args) {
        //建立一個set集合
        //不排序
        //Set<Integer> set = new HashSet<>();
        //排序
        Set<Integer> set = new TreeSet<>();
        //建立一個隨機數物件
        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);
        }
    }
}