Java基础第十三天

集合框架

Set 接口

Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素,而且Set接口中元素无序。
什么是哈希表呢?
哈希表底层使用的也是数组机制,数组中存放对象,这些对象存放的位置比较特殊,当把这些对象存入数组中时,会根据这些对象的特有数据结合相应的算法(hashCode),计算出这个对象在数组中的位置,然后把这个对象存放在数组中。而这样的数组就称为哈希数组,即就是哈希表。


当向哈希表中存放元素时,会调用 Object类中的 hashCode方法。由于任何对象都是Object类的子类,所以任何对象有拥有这个方法。
这里需要注意,如果两个对象 hashCode方法算出结果一样,这样现象称为哈希冲突,这时会调用对象的equals 方法,比较这两个对象是不是同一个对象,如果equals方法返回的是 true,那么就不会把第二个对象存放在哈希表中,如果返回的是 false,就会把这个值存放在哈希表中。


保证Set集合元素的唯一,其实就是根据对象的 hashCode和 equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写 hashCode 和 equals方法建立属于当前对象的比较方式。

示例代码
public class Test01 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        //实例化一个Set对象
        Collection<String> c = new HashSet<>();
        c.add("134");
        c.add("43");
        c.add("56");
        c.add("75");
        c.add("123");
        c.add("109");
        c.add("134");//存储重复元素

        for(String a : c){
            System.out.println(a);
        }
        //输出结果与输入数据不相同,且不能输入相同数据
    }

}

HashSet存储自定义对象

当向HashSet集合中添加一个对象时,首先会调用该对象的 hashCode() 方法来确定元素的存储位置,然后再调用对象的equals() 方法来确定该位置有没有重复元素。

特点:不包含重复元素,自定义的类必须重写hashCode、equals方法 当使用HashSet存储自定义对象时,我们要重写 hashCode()和 equals()方法

因为:
当调用HashSet的 add()方法,会先判断 hashCode()是否相同, 如果相同,就会再判断 equals(),如果 equals()也相同,那么就不存

代码示例
public class Test02 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        // 实例化一个 Set 
        Set<Student> stuSet = new HashSet<>(); 
        // 填充元素 
        stuSet.add(new Student("刘德华", 20)); 
        stuSet.add(new Student("张学友", 22)); 
        stuSet.add(new Student("黎明", 24)); 
        stuSet.add(new Student("郭富城", 26)); 
        // 添加重复元素 
        stuSet.add(new Student("郭富城", 26)); 

        // 遍历 
        for (Student stu : stuSet) { 
          System.out.println(stu.getName() + "  " + stu.getAge());// 取出时,跟存入的顺序不同; 
        } 
    }

}
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(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Student other = (Student) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }


}

注意:可用快捷键生成

LinkedHashSet

LinkedHashSet 是一个特例集合,元素存取有序,使用链表维护元素的顺序,这样使得元素看起来是以插入的顺序保存的,取出时会按照元素的添加顺序来访问集合里的元素。

示例代码
public class Test03 {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        LinkedHashSet<String> lh = new LinkedHashSet<>();
        lh.add("张三");
        lh.add("王五");
        lh.add("李四");
        lh.add("赵六");
        lh.add("燕七");
        lh.add("燕七");
        for (String s : lh) {
            System.out.println(s);
        }
    }
}

TreeSet集合

TreeSet 因为实现了 Set 接口,所以其集合特点是:元素不重复。TreeSet 里面存放的元素是按照元素的自然顺序来排列的 。

代码示例
public class Test04 {
    public static void main(String[] args) {
        // 实例化一个 TreeSet
        TreeSet<String> strSet = new TreeSet<>();
        // 填充集合
        strSet.add("ddd");
        strSet.add("aaa");
        strSet.add("xxx");
        strSet.add("ccc");
        strSet.add("bbb");
        strSet.add("yyy");
        strSet.add("fff");

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

TreeSet 内部采用平衡二叉树来存储元素,这样的结构可以保证 TreeSet 集合中没有重复的元素,并且可以对元素进行排序,其保证元素唯一性是通过元素的 compareTo 方法的返回值来确定

如存入: 22 20 25 24 19 20 26
1. 把第一个数据看成是根节点,从第二个数据开始,每个数据都从根节点开始比较

大于0: 就往右放
小于0: 就往左放
等于0: 说明是重复的元素

  1. 遍历从根节点开始看,按照左中右方式遍历

TreeSet类存储自定义对象

集合数据在进行比较时,都会调用 compareTo()方法,该方法是 Comparable 接口中定义的,因此想要对集合中的数据进行排序,就必须实现 Comparable接口

示例代码:新建学生类,并将学生对象存入TreeSet集合中
public class Test05 { 
  public static void main(String[] args) { 
    TreeSet<Student> stuSet = new TreeSet<>(); 
    stuSet.add(new Student("张三", 20));// TreeSet的 add()方法中,自动调用元素的compareTo()方法 
    stuSet.add(new Student("李四", 22)); 
    stuSet.add(new Student("王五", 24)); 
    stuSet.add(new Student("赵六", 22)); 
    // 遍历 
    for (Student stu : stuSet) { 
      System.out.println(stu.getName() + "," + stu.getAge()); 
    } 
  } 
}

注意:此代码会报错,无法排序。

使用自然排序(Comparable)接口

TreeSet 在进行数据存储时,都会对该对象进行排序,但是集合不知道该以什么样的方式进行排序,所以自定义类中需要实现Comparable接口,并在 compareTo 方法内实现排序比较。该方法用来比较元素的大小关系,然后将集合元素按照升序排列。

  • compareTo(T o) 方法

将此对象与指定的对象进行比较,以返回一个负整数、零或一个正整数,因为这个对象小于、等于或大于指定的对象。
参数 : o要比较的对象。
结果 : 一个负整数,零,或一个正整数,因为这个对象小于,等于,或大于指定的对象。

示例代码 :根据年龄比较
public class Test05 { 
  public static void main(String[] args) { 
    TreeSet<Student> stuSet = new TreeSet<>(); 
    stuSet.add(new Student("张三", 20));// TreeSet的 add()方法中,自动调用元素的compareTo()方法 
    stuSet.add(new Student("李四", 22)); 
    stuSet.add(new Student("王五", 24)); 
    stuSet.add(new Student("赵六", 22)); 
    // 遍历 
    for (Student stu : stuSet) { 
      System.out.println(stu.name+ "," + stu.age); 
    } 
  } 
}
class Student implements Comparable<Student>{ 
      String name; 
      int age; 
      public Student(String name, int age) { 
        super(); 
        this.name = name; 
        this.age = age; 
      } 

      //obj1.comparTo(obj2),如果该方法返回0,则表明这两个对象相等;如果返回一个正整数,则表明obj1大于 obj2;如果该方法返回一个负整数,则表明obj1小于obj2. 
      @Override 
      public int compareTo(Student o) { 
        System.out.println("this  " + name); 
        System.out.println("o  " + o.name);      
        //如果该方法返回 0则表明这两个对象相等 
        //如果返回一个正整数,则表明当前对象大于 o 
        //如果该方法返回一个负整数,则表明当前对象小于 o 
        return this.age - o.age; 
      } 
} 

上面比较的代码还有一点小问题:因为根据年龄进行比较的,如果数据中出现了年龄相同的两个对象,那么就会有不添加的效果,所以最完整的比较应该是:先比较年龄,如果年龄相同,那么对姓名进行排序,如果名字也相同,那么认为是相同的对象,不添加,
下面为排序代码:

示例代码
public class Test05 { 
  public static void main(String[] args) { 
    TreeSet<Student> stuSet = new TreeSet<>(); 
    stuSet.add(new Student("张三", 20));// TreeSet的 add()方法中,自动调用元素的compareTo()方法 
    stuSet.add(new Student("李四", 22)); 
    stuSet.add(new Student("王五", 24)); 
    stuSet.add(new Student("赵六", 22)); 
    // 遍历 
    for (Student stu : stuSet) { 
      System.out.println(stu.name+ "," + stu.age); 
    } 
  } 
}
class Student implements Comparable<Student>{ 
      String name; 
      int age; 
      public Student(String name, int age) { 
        super(); 
        this.name = name; 
        this.age = age; 
      } 

      //obj1.comparTo(obj2),如果该方法返回0,则表明这两个对象相等;如果返回一个正整数,则表明obj1大于 obj2;如果该方法返回一个负整数,则表明obj1小于obj2. 
      @Override 
//    public int compareTo(Student o) { 
//      System.out.println("this  " + name); 
//      System.out.println("o  " + o.name);      
//      //如果该方法返回 0则表明这两个对象相等 
//      //如果返回一个正整数,则表明当前对象大于 o 
//      //如果该方法返回一个负整数,则表明当前对象小于 o 
//      return this.age - o.age; 
//    }
      public int compareTo(Student o) { 
          System.out.println("this.name = " + this.name + " o.name = " + o.name); 
          //1.先按姓名排序 
          int n1 = this.name.compareTo(o.name); 
          //2.如果姓名相同,按年龄排序 
          int n2 = (n1 == 0 ? this.age - o.age : n1); 
          return n2; 
        } 
} 

注意:只要放在 TreeSet 中的元素对象,在该对象的类中必须实现 Comparable 接口,必须覆盖该接口中的compareTo()方法,并在该方法中编写比较规则。

通过构造方法传入排序比较器

比较器排序,也叫定制排序通过使用该比较器,比较时需要创建第三方类,实现 Comparator接口,并且覆盖其中的 compare()方法,编写比较规则和排序方式。

方法int compare(T o1, T o2)  

比较其两个顺序的参数。返回一个负整数、零或一个正整数作为第一个参数小于、等于或大于第二个参数。
比较器通过构造方法传入

示例代码
public class Test06 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        // 实例化一个 TreeSet
        TreeSet<Student> stuSet = new TreeSet<>(new MyComparator());
        stuSet.add(new Student("李四", 20));
        stuSet.add(new Student("张三", 22));
        stuSet.add(new Student("王五", 24));
        for (Student stu : stuSet) {
            System.out.println(stu.name + "," + stu.age);
        }
    }
}

// 排序比较器
class MyComparator implements Comparator<Student> {
    public int compare(Student o1, Student o2) {
        System.out.println("o1.name = " + o1.name + " o2.name = " + o2.name);
        // 先按姓名比
        int n1 = o1.name.compareTo(o2.name);
        // 如果姓名相同,比较年龄
        int n2 = (n1 == 0 ? o1.age - o2.age : n1);
        return n2;

    }

}

总结

  1. 当向TreeSet里面添加不是自定义对象时,元素会默认使用自然排序输出从小到大序列。(因为Java提供的对象实现了比较的接口)
  2. 当向TreeSet里面添加自定义的对象时,可以使用这2比较方式进行比较,效果上没有区别。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值