Java 集合(三) Set

Java 集合(三) Set

Set 系列集合的底层就是 Map 实现的,只是 Set 集合中的元素只要键数据(因为键是唯一的),不要值数据而已。

Set 集合对比

无序:存取顺序不一致

不重复:可以去除重复

无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。

  • HashSet : 无序、不重复、无索引。

  • LinkedHashSet:有序、不重复、无索引。

  • TreeSet:排序、不重复、无索引。

HashSet

HashSet 底层采用 哈希表 存储数据,哈希表是一种对于 增删改查数据性能都比较好 的结构。

HashSet 集合判断两个元素相等的标准是两个对象通过 equals() 方法比较相等,并且两个对象的 hashCode() 方法返回值也相等。

public class UseHashSet {
    public static void main(String[] args) {
        // 使用多态的方法创建一个 HashSet 对象
        Set<String> set = new HashSet<>();
        set.add("Simon");
        set.add("Alex");
        set.add("Robbie");
        // 重复数据将不会被保存
        set.add("Robbie");
        System.out.println(set);
    }
}

输出结果:

[Alex, Simon, Robbie]

由输出结果可以看出,其在集合中存储的位置是无序的(即:不是按照输入顺序来保存的)。

哈希表

JDK8之前的,底层使用 数组+链表 组成。

JDK8开始后,底层采用 数组+ 链表+红黑树 组成。

均使用 链地址法 解决冲突。

image-20220605111947372image-20220605112016871

LinkedHashSet

LinkedHashSet 是 HashSet 的子类,其原理是在 HashSet 的基础之上为每个元素添加了一个 前趋指针和后继指针 以便于顺序输出元素。

当遍历 LinkedHashSet 集合里的元素时,LinkedHashSet 会 按元素的添加顺序 来访问集合里的元素。但是由于要维护元素的插入顺序,在 性能上略低与HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。

image-20220606172941049

public class UseLinkedHashSet {
    public static void main(String[] args) {
        // 使用多态的方法创建一个 LinkedHashSet 对象
        Set<String> set = new LinkedHashSet<>();
        set.add("Simon");
        set.add("Alex");
        set.add("Robbie");
        // 重复数据将不会被保存
        set.add("Robbie");
        System.out.println(set);
    }
}

输出结果为:

[Simon, Alex, Robbie]

由输出结果可以看出,LinkedHashSet 的输出顺序是有序的。

TreeSet

TreeSet 的特点是所保存的元素是 按照一定规则进行排序 的。

TreeSet 的底层基于 红黑树 实现,其 增删改查性能都很好

TreeSet 的排序规则

① TreeSet 默认排序规则为:

  • 对于数值类型:Integer , Double,官方默认按照大小进行升序排序。

  • 对于字符串类型:默认按照首字符的编号升序排序。

  • 对于自定义类型如 Student 对象,TreeSet 无法直接排序。(想要使用 TreeSet 存储自定义类型,需要制定排序规则

② 自定义排序规则

  • 方法一:让自定义的类(如学生类)实现 Comparable 接口重写里面的 compareTo 方法来定制比较规则。
  • 方法二:TreeSet 集合有 参数构造器,可以设置 Comparator 接口对应的比较器对象,来定制比较规则。

案例1:TreeSet 的默认排序规则

public class UseTreeSetByDefault {
    public static void main(String[] args) {
        // 创建两个 TreeSet 对象
        Set<Double> doubleSet = new TreeSet<>();
        Set<String> stringSet = new TreeSet<>();
        // 为 TreeSet 对象赋值,Double 类型按照升序排序,String 类型按照 ASCII 码排序
        Collections.addAll(doubleSet, 12.3, 71.2, 64.5, 33.2);
        Collections.addAll(stringSet, "Simon", "Alex","alex" ,"Robbie");
        // 输出集合
        System.out.println(doubleSet);
        System.out.println(stringSet);
    }
}

输出结果为:

[12.3, 33.2, 64.5, 71.2]
[Alex, Robbie, Simon, alex]

案例2:TreeSet 的自定义排序之在类中重写 compareTo

创建一个学生类,实现 Compareable 接口,并改写 compareTo 方法。

// 创建一个学生类,实现 Compareable 接口
public class Student implements Comparable<Student>{
    private String name;
    private int age;

    // 改写 compareTo 方法
    @Override
    public int compareTo(Student stu) {
        // 根据年龄升序排序
        return this.getAge() - stu.getAge();
    }

    // 有参构造器
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 无参构造器
    public Student() {
    }

    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;
    }
    
	// 重写 toString 方法以便格式化输出
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

创建一个 Student 类的 TreeSet 对象

public class UseTreeSetByCompareable {
    public static void main(String[] args) {
        // 创建一个 TreeSet 对象
        Set<Student> studentSet = new TreeSet<>();
        // 为集合添加 Student 类型的元素(通过有参构造器接受)
        studentSet.add(new Student("Simon", 21));
        studentSet.add(new Student("Robbie", 23));
        studentSet.add(new Student("Alex", 22));
        System.out.println(studentSet);
    }
}

输出结果:

[Student{name=‘Simon’, age=21}, Student{name=‘Alex’, age=22}, Student{name=‘Robbie’, age=23}]

由于改写了 compareTo 的规则,输出按照年龄升序输出。

案例3:TreeSet 的自定义排序之通过参数构造器排序

public class UseTreeSetByConstructor {
    public static void main(String[] args) {
        // 创建一个 TreeSet 对象,并重新 compare 方法
        TreeSet<Student> studentSet = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o2.getAge() - o1.getAge();
            }
        });
        // 为集合添加 Student 类型的元素(通过有参构造器接受)
        studentSet.add(new Student("Simon", 21));
        studentSet.add(new Student("Robbie", 23));
        studentSet.add(new Student("Alex", 22));
        System.out.println(studentSet);
    }
}

使用 Lambda 表达式书写:

public class UseTreeSetByConstructor {
    public static void main(String[] args) {
        // 创建一个 TreeSet 对象,并重新 compare 方法
        TreeSet<Student> studentSet = new TreeSet<>( (o1, o2) -> (o2.getAge() - o1.getAge()) );
        // 为集合添加 Student 类型的元素(通过有参构造器接受)
        studentSet.add(new Student("Simon", 21));
        studentSet.add(new Student("Robbie", 23));
        studentSet.add(new Student("Alex", 22));
        System.out.println(studentSet);
    }
}

输出结果为:

[Student{name=‘Robbie’, age=23}, Student{name=‘Alex’, age=22}, Student{name=‘Simon’, age=21}]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值