【Java】11 Set 集合

时间:2022-07-26
本文章向大家介绍【Java】11 Set 集合,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

java.util.Set 接口和 java.util.List 接口一样,同样继承自 Collection 接口,它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Collection 接口更加严格了。与 List 接口不同的是,Set 接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。

1.1 Set 接口

   Set 接口继承了 Collection 接口,因此可以使用 Collection 接口中的所有方法。由于 Set 集合中的元素不能重复,因此在向 Set 集合中添加元素时,需要先判断新增元素是否已经存在于集合中,再确定是否执行添加操作。

1.2 HashSet 集合

   HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时就是使用这个实现类。HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能。    当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode( ) 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals( ) 方法比较返回 true,但它们的 hashCode( ) 方法返回值不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。也就是说,HashSet 集合判断两个元素相等的标准是两个对象通过 equals( ) 方法比较相等,并且两个对象的 hashCode( ) 方法返回值也相等。

1.2.1 HashSet 的特点

   ♝ 集合元素值可以是 null。    ♝ 不能保证元素的排列顺序,顺序可能与添加顺序不同。    ♝ HashSet 不是同步的,如果多个线程同时访问一个 HashSet,假设有两个或者两个以上线程同时修改了 HashSet 集合时,则必须通过代码来保证其同步。

1.2.2 示例

set 集合存放 字符串

public class Test {

    public static void main(String[] args) {
        HashSet<String> set = new HashSet<String>();
        set.add("刘备");
        set.add("关羽");
        set.add("张飞");
        set.add("刘备");

        System.out.println(set);
    }
}

set 存放 对象

// 学生类
public class Stu {
    private String name;
    private Integer age;

    public Stu(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Stu{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}


----------------------------------------------------------------------

// 测试类
public class Test {
    public static void main(String[] args) {
        HashSet<Stu> set = new HashSet<>();
        set.add(new Stu("刘备", 1000));
        set.add(new Stu("刘备", 1000));

        System.out.println(set);
    }
}

   为什么同的对象都会存到 set 集合,我们在 Stu 类中没有重写 equals、hashcode 方法,用的都是 Object 中的方法,当使用 Object 中的 equals 方法时,比较的是地址值,被认为不是一样的,这将导致 HashSet 会把这两个对象保存在 Hash 表的不同位置,从而使两个对象都可以添加成功。

set 存放 对象,重写 equals、hashcode

// 学生类
public class Stu {
    private String name;
    private Integer age;

    public Stu(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Stu stu = (Stu) o;
        return name.equals(stu.name) &&
                age.equals(stu.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Stu{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}


----------------------------------------------------------------------

// 测试类
public class Test {
    public static void main(String[] args) {
        HashSet<Stu> set = new HashSet<>();
        set.add(new Stu("刘备", 1000));
        set.add(new Stu("刘备", 1000));

        System.out.println(set);
    }
}

1.2.2 注意事项

   如果需要把某个类的对象保存到 HashSet 集合中,必须重写这个类的 equals( ) 方法和 hashCode( ) 方法时,如果两个对象通过 equals( ) 方法比较返回 true,这两个对象的 hashCode 值也应该相同。

1.3 LinkedHashSet 集合

   HashSet 还有一个子类 LinkedHashSet,LinkedHashSet 集合也是根据元素的 hashCode 值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。也就是说,当遍 历LinkedHashSet 集合里的元素时,LinkedHashSet 将会按元素的添加顺序来访问集合里的元素。    LinkedHashSet 需要维护元素的插入顺序,因此性能略低于 HashSet 的性能,但在迭代访问 Set 里的全部元素时将有很好的性能,因为它以链表来维护内部顺序。    虽然 LinkedHashSet 使用了链表记录集合元素的添加顺序,但 LinkedHashSet 依然是 HashSet,因此它依然不允许集合元素重复(由哈希表保证唯一性,链表保证存取一致)。

1.4 TreeSet 集合

   TreeSet 是 SortedSet 接口的实现类,TreeSet可以确保集合元素处于排序状态。

1.4.1 常用方法

方法名

说明

Comparator comparator( )

如果 TreeSet 采用了定制排序,则该方法返回定制排序所使用的 Comparator;如果 TreeSet 采用了自然排序,则返回 null

Object first( )

返回集合中的第一个元素

Object last( )

返回集合中的最后一个元素

Object lower(Object e)

返回集合中位于指定元素之前的元素

Object higher(Object e)

返回集合中位于指定元素之后的元素

1.4.2 注意

   如果把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口,否则程序将会抛出异常。


补充:可变参数

   在 JDK1.5 之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式:修饰符 返回值类型 方法名(参数类型... 形参名){ } 底层使用数组实现,可以当作数组使用,完全等价与修饰符 返回值类型 方法名(参数类型[] 形参名){ }只是后面这种定义,在调用时必须传递数组,而前者可以直接传递数据即可。

示例

public class ChangeArgs {
	public static void main(String[] args) {
		int[] arr = { 1, 4, 62, 431, 2 };
        // 传递数组
        System.out.println("传递数组: " + getSum(arr));
        // 传递数据
        System.out.println("传递数据: " + getSum( 1, 4, 62, 431, 2 ));
	}
	
	public static int getSum(int... arr) {
		int sum = 0;
		for (int a : arr) {
		    sum += a;
		}
		return sum;
	}
}

注意   ♜ 一个参数列表只能由一个可变参数   ♜ 如果由多个参数,可变参数位于最后