Java集合中的Set集合必备文章

  

概述

Set接口作为Collection的子接口,按理来说应该在原来接口的基础增加更多的方法,但是Set这个子接口呢,并没有提供额外的方法,只不过是在原来的基础上,对数据的要求更加严格了。

Set接口的特点:

  • 无序性:Set集合中的元素是不要求有顺序的
  • 不可重复性:Set集合中的元素是不能出现重复的
  • 无索引:Set集合中的元素是没有索引的(特定的顺序编号)
Set的使用 常用方法介绍

既然Set没有在Collection的基础上添加新的方法,那么其常用方法也是可以参考Collection的常用方法的
Collection的常用方法可参考本文

Set集合的遍历

与Collection集合的遍历方式一样,Set集合也是支持foreach和Iterator两种方式遍历元素的。

代码实例
package com.atguigu.demo.Set;

import java.util.Iterator;
import java.util.linkedHashSet;

public class linkedSet {
    public static void main(String[] args) {
        //创建linkedHashSet集合
        linkedHashSet<String> set = new linkedHashSet<>();

        //添加元素
        set.add("张三");
        set.add("李四");
        set.add("王五");
        set.add("张三");

        //元素个数
        System.out.println("元素个数:" + set.size());

        //增强for遍历元素
        for (String name : set) {
            System.out.println(name);
        }

        //使用iterator
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }
}
Set的实现类

Set接口有很多的实现类,下面我们介绍下其常用的实现类: HashSet、TreeSet、linkedHashSet

HashSet 基本介绍

HashSet是Set接口最常使用的实现类,其底层是通过HashMap实现的。(HashMap可以看这个文章)

源码如下:

    
    public HashSet() {
        map = new HashMap<>();
    }

HashMap的底层物理实现是一个Hash表,那这样的话,存入元素的时候,就会根据Hash算法来存储了。即当向HashSet集合中存入一个元素时,HashSet会先判断该对象的hashCode值,然后根据hashCode值决定该对象在HashSet中的存储位置:

  • 如果hashCode值不同,直接把该元素存储到hashCode()指定的位置
  • 如果hashCode 值相同,那么会继续判断该元素和集合对象的equals()作比较。

所以存储到HashSet的元素要重写hashCode和equals方法。

代码示例
package com.atguigu.demo.Set;


public class Employee {

    //自定义属性
    private String name;
    private MyDate birthday;

    //构造器
    public Employee(String name, MyDate birthday) {
        super();
        this.name = name;
        this.birthday = birthday;
    }
    public Employee() {
        super();
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public MyDate getBirthday() {
        return birthday;
    }
    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    //重写hashCode
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((birthday == null) ? 0 : birthday.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    //重写equals
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (birthday == null) {
            if (other.birthday != null)
                return false;
        } else if (!birthday.equals(other.birthday))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
    @Override
    public String toString() {
        return "姓名:" + name + ", 生日:" + birthday;
    }
}

//hashSet测试
import java.util.HashSet;

public class TestHashSet {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        HashSet<Employee> set = new HashSet<>(); //创建HashSet集合

        set.add(new Employee("张三", new MyDate(1990,1,1)));

        //重复元素无法添加,因为MyDate和Employee重写了hashCode和equals方法
        set.add(new Employee("张三", new MyDate(1990,1,1)));
        set.add(new Employee("李四", new MyDate(1992,2,2)));

        for (Employee object : set) {
            System.out.println(object);
        }
    }
}

linkedHashSet 基本介绍

linkedHashSet是HashSet的子类,所以它的很多东西都是继承自HashSet的,这里不再说明,但是相对于HashSet,它在结点中增加两个属性before和after维护了结点的前后添加顺序,这是因为其底层采用了链表+哈希表的结构。

源码如下:

   HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new linkedHashMap<>(initialCapacity, loadFactor);
    }

所以linkedHashSet可以保证元素的插入顺序。

因为维护了一个链表,存储、查找的性能略低于HashSet,但遍历时性能高于HashSet(根据链表进行遍历)。

代码示例

import java.util.Iterator;
import java.util.linkedHashSet;

public class linkedHashSet {
    public static void main(String[] args) {
        //创建linkedHashSet集合
        linkedHashSet<String> set = new linkedHashSet<>();

        //添加元素
        set.add("张三");
        set.add("李四");
        set.add("王五");
        set.add("张三");

        //元素个数
        System.out.println("元素个数:" + set.size());

        //增强for遍历元素
        for (String name : set) {
            System.out.println(name);
        }
    }
}
TreeSet 基本介绍

与前面两种实现类不同,TreeSet底层是基于红黑树结构实现的,另外TreeSet里的元素是有序的,而实现这个有序的特征,是需要TreeSet里的元素实现排序,其实现排序的形式有两种:自然排序和定制排序。

因为放入其中的元素的类必须实现Comparable接口,所以在元素不能为null。

自然排序

这个默认的排序,让待添加的元素类型实现Comparable接口,并重写compareTo方法(数值型按数值大小排列,字符按码值排列,Date、Time按时间戳的大小排列…默认升序)
对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj) 方法比较返回值为0。

定制排序

如果自然排序(Comparable)规则不符合我们的排序需求,或者元素的类型没有实现Comparable接口。那么在创建TreeSet时,可以单独指定一个Comparator的对象,根据我们自己的需求进行自定义排序。
使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0。

代码示例

public class Student {

    //自定义属性
    private int id;
    private String name;

    //构造器
    public Student(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    //省略了name属性的get/set

    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + "]";
    }
}



import org.junit.Test;

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

public class TestTreeSet {

    
    @Test
    public void test1(){
        //MyDate myDate = new MyDate(1990,1,1);
        //创建TreeSet集合
        TreeSet<String> set = new TreeSet<>();
        //TreeSet set2 = new TreeSet<>(); 	//ClassCastException:com.atguigu.demo.Set.MyDate cannot be cast to java.lang.Comparable

        //添加元素
        set.add("zhangsan");  //String它实现了java.lang.Comparable接口
        set.add("zisi");
        set.add("wangwu");
        set.add("hangsan");

        //set2.add(myDate);
        //System.out.println(set2);

        System.out.println("元素个数:" + set.size());
        for (String str : set) {
            System.out.println(str);
        }
    }


    
    @Test
    public void test2(){

        //创建集合,并指定Comparator的对象
        TreeSet<Student> set = new TreeSet(new Comparator<Student>(){ //那么在创建TreeSet时,可以单独指定一个Comparator的对象
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getId() - o2.getId();
            }

        });

        //添加学生
        set.add(new Student(3,"张三"));
        set.add(new Student(1,"李四"));
        set.add(new Student(2,"王五"));
        set.add(new Student(3,"张三风"));

        //元素个数
        System.out.println("元素个数:" + set.size());

        //遍历
        for (Student stu : set) {
            System.out.println(stu);
        }
    }
}

 文章转自:吃透Java集合中的Set集合必备文章,快快收藏_Java-考高分网 (kaotop.com)
上一篇:2022Java学习笔记七十九(网络编程:TCP通信,TCP通信:多个客户端消息【重点】,追踪客户端的上线和下线功能、线程池优化)


下一篇:java8 reduce方法三个参数情况下的理解和示例