java笔记6——Set的三个子类

目录

一、Collection的回顾

二、Set接口

      1)概述

      2)三个子类

三、HashSet的存储

1. HashSet的存储字符串并遍历

2.HashSet存储自定义对象并遍历

四、TreeSet的介绍和使用

1.TreeSet能够对元素按照某种规则进行排序

         A:自然排序

         B:比较器排序

  2.TreeSet根据比较的返回值是否为0来决定元素的唯一性

五、TreeSet比较器排序匿名内部类实现实例

六、总结

  1.针对Collection集合我们到底使用谁呢?(掌握)

2.在集合中常见的数据结构(掌握)


一、Collection的回顾

Collection

                   |——————List

                                               有序(存储顺序和取出顺序一致),元素可重复

                   |——————Set

                                               无序(存储顺序和取出顺序不一致),元素唯一

二、Set接口

      1)概述

          一个不包含重复元素的Collection

      2)三个子类

         a)

           i.HashSet的概述

               不保证set的迭代顺序

               特别是不保证该顺序恒久不变

           ii.HashSet如何保证元素的唯一性?

                  底层数据结构是哈希表(元素是链表的数组)

                  哈希表依赖于哈希值存储

                  添加功能底层依赖两个方法:

                            int hashCode();

                            boolean equals(Object obj)

         b)LinkedHashSet的概述

                元素有序,唯一

                底层数据结构是链表和哈希表

                由链表保证元素有序(存储和取出顺序一致)

                由哈希表保证元素唯一

          c)

                i)TreeSet的概述

                     使用元素的自然顺序对元素进行排序

                     或者根据创建set时提供的Comparator进行排序

                     具体取决于使用的构造方法

                ii)TreeSet如何保证元素的排序和唯一性的

                     底层数据结构是红黑树(红黑树是一种自平衡的二叉树)

三、HashSet的存储

1. HashSet的存储字符串并遍历

(参考刘意老师的代码)

/*
 * HashSet:存储字符串并遍历
 * 问题:为什么存储字符串的时候,字符串内容相同的只存储了一个呢?
 * 通过查看add方法的源码,我们知道这个方法底层依赖 两个方法:hashCode()和equals()。
 * 步骤:
 *             首先比较哈希值
 *             如果相同,继续走,比较地址值或者走equals()
 *             如果不同,就直接添加到集合中  
 * 按照方法的步骤来说: 
 *             先看hashCode()值是否相同
 *                      相同:继续走equals()方法
 *                               返回true:     说明元素重复,就不添加
 *                               返回false:说明元素不重复,就添加到集合
 *                      不同:就直接把元素添加到集合
 * 如果类没有重写这两个方法,默认使用的Object()。一般来说不同相同。
 * 而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉。只留下一个。
 */

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

                   // 创建并添加元素
                   hs.add("hello");
                   hs.add("world");
                   hs.add("java");
                   hs.add("world");

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

2.HashSet存储自定义对象并遍历

/*
 * 需求:存储自定义对象,并保证元素的唯一性
 * 要求:如果两个对象的成员变量值都相同,则为同一个元素。
 *
 * 目前是不符合我的要求的:因为我们知道HashSet底层依赖的是hashCode()和equals()方法。
 * 而这两个方法我们在学生类中没有重写,所以,默认使用的是Object类。
 * 这个时候,他们的哈希值是不会一样的,根本就不会继续判断,执行了添加操作。
 */

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

                   // 创建学生对象
                   Student s1 = new Student("林青霞", 27);
                   Student s2 = new Student("柳岩", 22);
                   Student s3 = new Student("王祖贤", 30);
                   Student s4 = new Student("林青霞", 27);
                   Student s5 = new Student("林青霞", 20);
                   Student s6 = new Student("范冰冰", 22);

                   // 添加元素
                   hs.add(s1);
                   hs.add(s2);
                   hs.add(s3);
                   hs.add(s4);
                   hs.add(s5);
                   hs.add(s6);

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

四、TreeSet的介绍和使用

1.TreeSet能够对元素按照某种规则进行排序

    排序方式有两种

         A:自然排序

1)TreeSet存储Integer类型数据并保证唯一性和排序

排序就是自然排序,按照从小到大的排序方式进行输出

2)TreeSet存储自定义对象并保证排序和唯一

         例如:

// 创建集合对象

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

                   // 创建元素
                   Student s1 = new Student("linqingxia", 27);
                   Student s2 = new Student("zhangguorong", 29);
                   Student s3 = new Student("wanglihong", 23);
                   Student s4 = new Student("linqingxia", 27);
                   Student s5 = new Student("liushishi", 22);
                   Student s6 = new Student("wuqilong", 40);
                   Student s7 = new Student("fengqingy", 22);


                   // 添加元素
                   ts.add(s1);
                   ts.add(s2);
                   ts.add(s3);
                   ts.add(s4);
                   ts.add(s5);
                   ts.add(s6);
                   ts.add(s7);

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

会报错:

Exception in thread "main" java.lang.ClassCastException: cn.itcast_05.Student cannot be cast to java.lang.Comparable

这是因为,此时我们并没有告诉其排序方式,所以无法进行排序。

并且,元素什么情况下才是唯一呢?也没有告诉程序,所以也没办法保证。

那么,告诉程序,1.自然排序,按照年龄从大到小排序。

                             2.成员变量值相同即为同一个元素。

问题就出现了:怎样让自定义对象实现自然排序呢?

答案是:如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口。

具体做法如下:

// Comparabl<>接口,该接口对实现它的每个类的对象强加一个整体排序。 
//这个排序被称为类的自然排序 ,类的compareTo方法被称为其自然比较方法 。

public class Student implements Comparable<Student> {

         private String name;
         private int age;

         public Student() {
                   super();
         }

         public Student(String name, int age) {
                   super();
                   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) {
                   // 这里返回什么,其实应该根据我的排序规则来做
                   // 按照年龄排序,主要条件
                   int num = this.age - s.age;
                   // 次要条件
                   // 年龄相同的时候,还得去看姓名是否也相同
                   // 如果年龄和姓名都相同,才是同一个元素
                   int num2 = num == 0 ? this.name.compareTo(s.name) : num;
                   return num2;
         }
}

         B:比较器排序

关于比较器排序的使用和实现,有两种方式:

第一种是创建一个比较器外部类:

public class TreeSetDemo {
         public static void main(String[] args) {
                   // 创建集合对象

                  // public TreeSet(Comparator comparator) //比较器排序

TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());

                   // 创建元素
                   Student s1 = new Student("linqingxia", 27);
                   Student s2 = new Student("zhangguorong", 29);
                   Student s3 = new Student("wanglihong", 23);
                   Student s4 = new Student("linqingxia", 27);
                   Student s5 = new Student("liushishi", 22);
                   Student s6 = new Student("wuqilong", 40);
                   Student s7 = new Student("fengqingy", 22);
                   Student s8 = new Student("linqingxia", 29);

                   // 添加元素
                   ts.add(s1);
                   ts.add(s2);
                   ts.add(s3);
                   ts.add(s4);
                   ts.add(s5);
                   ts.add(s6);
                   ts.add(s7);
                   ts.add(s8);

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

外部类:

public class MyComparator implements Comparator<Student> {
         @Override
         public int compare(Student s1, Student s2) {
                   // int num = this.name.length() - s.name.length();
                   // this -- s1
                   // s -- s2
                   // 姓名长度
                   int num = s1.getName().length() - s2.getName().length();
                   // 姓名内容
                   int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                   // 年龄
                   int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
                   return num3;
         }
}

第二种是直接在类中使用匿名内部类:

public class TreeSetDemo {

         public static void main(String[] args) {

                   // 创建集合对象
                   // 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象
                   // 而匿名内部类就可以实现这个东西
                   TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {

                            @Override
                   public int compare(Student s1, Student s2) {

                        // 姓名长度
                        int num = s1.getName().length() - s2.getName().length();

                        // 姓名内容
                        int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;

                        // 年龄
                        int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
                                     return num3;
                            }
                   });

                   // 创建元素
                   Student s1 = new Student("linqingxia", 27);
                   Student s2 = new Student("zhangguorong", 29);
                   Student s3 = new Student("wanglihong", 23);
                   Student s4 = new Student("linqingxia", 27);
                   Student s5 = new Student("liushishi", 22);
                   Student s6 = new Student("wuqilong", 40);
                   Student s7 = new Student("fengqingy", 22);
                   Student s8 = new Student("linqingxia", 29);

                   // 添加元素
                   ts.add(s1);
                   ts.add(s2);
                   ts.add(s3);
                   ts.add(s4);
                   ts.add(s5);
                   ts.add(s6);
                   ts.add(s7);
                   ts.add(s8);

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

  2.TreeSet根据比较的返回值是否为0来决定元素的唯一性

五、TreeSet比较器排序匿名内部类实现实例

import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;

/*
 * 键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
 *
 * 分析:
 *             A:定义学生类
 *             B:创建一个TreeSet集合
 *             C:总分从高到底如何实现呢?                
 *             D:键盘录入5个学生信息
 *             E:遍历TreeSet集合
 */

public class TreeSetDemo {

         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 ? s1.getChinese() - s2.getChinese() : num;

          // 总分相同的不一定数序相同
          int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;

          // 总分相同的不一定英语相同
          int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3;

          // 姓名还不一定相同呢
          int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName()): num4;

                                   return num5;
                            }
                   });

                   System.out.println("学生信息录入开始");
                   // 键盘录入5个学生信息
                   for (int x = 1; x <= 5; x++) {
                            Scanner sc = new Scanner(System.in);
                            System.out.println("请输入第" + x + "个学生的姓名:");
                            String name = sc.nextLine();
                            System.out.println("请输入第" + x + "个学生的语文成绩:");
                            String chineseString = sc.nextLine();
                            System.out.println("请输入第" + x + "个学生的数学成绩:");
                            String mathString = sc.nextLine();
                            System.out.println("请输入第" + x + "个学生的英语成绩:");
                            String englishString = sc.nextLine();

                            // 把数据封装到学生对象中
                            Student s = new Student();

                            s.setName(name);
                            s.setChinese(Integer.parseInt(chineseString));
                            s.setMath(Integer.parseInt(mathString))
                            s.setEnglish(Integer.parseInt(englishString));

                           // 把学生对象添加到集合
                            ts.add(s);
                   }
                   System.out.println("学生信息录入完毕");

                   System.out.println("学习信息从高到低排序如下:");
                   System.out.println("姓名\t语文成绩\t数学成绩\t英语成绩");

                   // 遍历集合
                   for (Student s : ts) {
                            System.out.println(s.getName() + "\t" + s.getChinese() + "\t"
                                               + s.getMath() + "\t" + s.getEnglish());
                   }
         }
}

六、总结

  1.针对Collection集合我们到底使用谁呢?(掌握)


    唯一吗?
        是:Set
            排序吗?
                是:TreeSet
                否:HashSet
        如果你知道是Set,但是不知道是哪个Set,就用HashSet。
            
        否:List
            要安全吗?
                是:Vector
                否:ArrayList或者LinkedList
                    查询多:ArrayList
                    增删多:LinkedList
        如果你知道是List,但是不知道是哪个List,就用ArrayList。   
    如果你知道是Collection集合,但是不知道使用谁,就用ArrayList。
    如果你知道用集合,就用ArrayList。

2.在集合中常见的数据结构(掌握)


    ArrayXxx:底层数据结构是数组,查询快,增删慢
    LinkedXxx:底层数据结构是链表,查询慢,增删快
    HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()
    TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序

猜你喜欢

转载自blog.csdn.net/elice_/article/details/82429524