Java集合框架|Collection体系的Set分支详解

本文详细介绍了Java集合框架中Set接口的基本概念及其两种实现类HashSet和TreeSet。解释了这两种实现类的特点,包括它们如何保证元素的唯一性、无序性和排序方式。此外,还提供了丰富的示例代码帮助理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇文章主要是概括讲解Collection的Set接口分支!

Set接口

Set的特点

  • 无序、无下标和元素不能重复。

Set的方法实现

class Character<T,K>{
    T name;
    K age;
    public Character(T name, K age) {
        this.name = name;
        this.age = age;
    }
    public T getName() {
        return name;
    }
    @Override
    public String toString() {
        return "Character{" +
                "name=" + name +
                ", age=" + age +
                '}';
    }
}
public class SetTest {
    public static void main(String[] args) {
        System.out.println("----add(string)-----");
        //Set集合的特点:无序、无下标和元素不能重复
        Set set1=new HashSet();
        set1.add("张三");
        set1.add("李四");
        set1.add("王五");
        System.out.println(set1);
        set1.add("张三");
        System.out.println(set1);

        System.out.println("----add(obj)-----");
        Set<Character> set2=new HashSet<>();
        Character<String,Integer> c1=new Character<>("张三",18);
        Character<String,Integer> c2=new Character<>("李四",19);
        Character<String,Integer> c3=new Character<>("王五",20);
        set2.add(c1);
        set2.add(c2);
        set2.add(c3);
        System.out.println(set2);
        set2.add(c3);
        System.out.println(set2);

        System.out.println("----remove(obj)-----");
        set2.remove(c3);
        System.out.println(set2);

        System.out.println("----contains(obj)-----");
        System.out.println(set2.contains(c2));
        System.out.println(set2.contains(c3));

        System.out.println("----遍历-----");
        //和list差不多,这里主要举iterator迭代器
        Iterator<Character> iterator = set2.iterator();
        while (iterator.hasNext()){
            Character next = iterator.next();
            System.out.printf("%s ",next.getName());
        }
    }
}
//输出结果:
----add(string)-----
[李四, 张三, 王五]
[李四, 张三, 王五]
----add(obj)-----
[Character{name=王五, age=20}, Character{name=李四, age=19}, Character{name=张三, age=18}]
[Character{name=王五, age=20}, Character{name=李四, age=19}, Character{name=张三, age=18}]
----remove(obj)-----
[Character{name=李四, age=19}, Character{name=张三, age=18}]
----contains(obj)-----
true
false
----遍历-----
李四 张三 

HashSet实现类

HashSet的方法实现

  • HashSet是基于HashMap实现的,比如HashSet中add方法调用的是底层HashMap中的put()方法。HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个固定对象private static final Object PRESENT = new Object()。
class SimpleNumber{
    int number;
    public SimpleNumber(int number) {
        this.number = number;
    }
    public int getNumber() {
        return number;
    }
    @Override
    public String toString() {
        return "SimpleNumber{" +
                "number=" + number +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SimpleNumber that = (SimpleNumber) o;
        return number == that.number;
    }
    @Override
    public int hashCode() {
        return Objects.hash(number);
    }
}
public class HashSetTest {
    public static void main(String[] args) {
        //HashSet的方法和Set接口的方法差不多
        HashSet hashSet=new HashSet();
        SimpleNumber s1=new SimpleNumber(8);
        SimpleNumber s2=new SimpleNumber(3);
        SimpleNumber s3=new SimpleNumber(4);
        hashSet.add(s1);
        hashSet.add(s2);
        hashSet.add(s3);
        System.out.println("原来的:"+hashSet);
        hashSet.add(s3);
        System.out.println("添加s3:"+hashSet);
        //如果重写了该类的hashCode方法和equals方法,那么就不会添加新的对象元素
        //如果没有重写或者只重写其中一个,新的元素还是会被添加进来
        //类似:[SimpleNumber{number=3},SimpleNumber{number=4}, SimpleNumber{number=4}, SimpleNumber{number=8}]
        hashSet.add(new SimpleNumber(4));
        System.out.println("添加新的对象:"+hashSet);

        //如果重写了该类的hashCode方法和equals方法,那么就会在集合中删除该元素
        //如果没有重写或者只重写其中一个,不会删除任何元素
        hashSet.remove(new SimpleNumber(4));
        System.out.println("删除操作:"+hashSet);
    }
}
//输出结果:
原来的:[SimpleNumber{number=3}, SimpleNumber{number=4}, SimpleNumber{number=8}]
添加s3:[SimpleNumber{number=3}, SimpleNumber{number=4}, SimpleNumber{number=8}]
添加新的对象:[SimpleNumber{number=3}, SimpleNumber{number=4}, SimpleNumber{number=8}]
删除操作:[SimpleNumber{number=3}, SimpleNumber{number=8}]

TreeSet实现类

  • TreeSet是基于TreeMap实现的,内部维持了一个简化版的TreeMap。

  • TreeSet实现了SortedSet接口,能对集合元素自动排序。

  • TreeSet元素对象的类型实现Comparable接口,指定排序规则。

TreeSet的方法实现

public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet<Integer> numbers = new TreeSet<>();
        numbers.add(5);
        numbers.add(2);
        numbers.add(9);
        numbers.add(4);
        numbers.add(6);
        numbers.add(7);
        System.out.println("TreeSet: " + numbers);

        // 使用 first() 方法
        int first = numbers.first();
        System.out.println("第一个数字: " + first);
        // 使用 last() 方法
        int last = numbers.last();
        System.out.println("最后一个数字: " + last);
        System.out.println("------------");

        // 使用 higher() 返回大于指定元素(element)的最小元素
        System.out.println("使用 higher: " + numbers.higher(4));
        // 使用 lower() 返回小于指定元素(element)的最大元素
        System.out.println("使用 lower: " + numbers.lower(4));
        // 使用 ceiling() 返回大于指定元素(element)的那些元素中的最小元素。如果传递的元素(element)存在于树集中,则返回作为参数传递的元素(element)
        System.out.println("使用 ceiling: " + numbers.ceiling(4));
        // 使用 floor() 返回小于指定元素(element)的元素中最大的元素。如果传递的元素(element)存在于树集中,则返回作为参数传递的元素(element)
        System.out.println("使用 floor: " + numbers.floor(3));
        System.out.println("------------");

        // 使用 pollFirst() 返回并从集合中删除第一个元素
        System.out.println("删除第一个元素: " + numbers.pollFirst());
        // 使用 pollLast() 返回并从集合中删除最后一个元素
        System.out.println("删除最后一个元素: " + numbers.pollLast());
        System.out.println("新的TreeSet: " + numbers);
        System.out.println("------------");

        /*
           headSet(element,booleanValue)
           headSet()方法返回指定元素(作为参数传递)之前的树集的所有元素
           booleanValue参数是可选的。默认值为false
           如果booleanValue的值为true,则该方法返回指定元素之前的所有元素,包括指定元素
         */

        // 使用 headSet()使用默认的布尔值
        System.out.println("headSet()使用默认的布尔值: " + numbers.headSet(5));
        // 使用 headSet()使用指定的布尔值
        System.out.println("headSet()带有布尔值: " + numbers.headSet(5, true));
        System.out.println("------------");

        /*
           tailSet(element,booleanValue)
           tailSet()方法返回包含指定元素的指定元素(作为参数传递)之后的树集的所有元素。
           booleanValue参数是可选的。默认值为true。
           如果false作为参数传递booleanValue,则该方法将返回指定后的所有元素,不包括指定的element
         */

        // 使用 tailSet()使用默认的布尔值
        System.out.println("tailSet()使用默认的布尔值: " + numbers.tailSet(5));
        // 使用 tailSet()使用指定的布尔值
        System.out.println("tailSet()带有布尔值: " + numbers.tailSet(5, false));
        System.out.println("------------");

        /*
           subSet(e1,bv1,e2,bv2)
           subSet()方法返回e1和e2之间的所有元素,包括e1。
           bv1和bv2是可选参数。  bv1的默认值为true,bv2的默认值为false。
           如果将false作为bv1传递,则该方法返回e1和e2之间的所有元素,而不包括e1。
           如果将true作为bv2传递,则该方法返回e1和e2之间的所有元素,包括e1。
         */
        // 使用 subSet()使用默认的布尔值
        System.out.println("subSet()使用默认布尔值: " + numbers.subSet(4, 6));
        // 使用 subSet()使用指定的布尔值
        System.out.println("subSet()使用指定的布尔值: " + numbers.subSet(4, false, 6, true));
    }
}
//输出结果:
TreeSet: [2, 4, 5, 6, 7, 9]
第一个数字: 2
最后一个数字: 9
------------
使用 higher: 5
使用 lower: 2
使用 ceiling: 4
使用 floor: 2
------------
删除第一个元素: 2
删除最后一个元素: 9
新的TreeSet: [4, 5, 6, 7]
------------
headSet()使用默认的布尔值: [4]
headSet()带有布尔值: [4, 5]
------------
tailSet()使用默认的布尔值: [5, 6, 7]
tailSet()带有布尔值: [6, 7]
------------
subSet()使用默认布尔值: [4, 5]
subSet()使用指定的布尔值: [5, 6]

实现Comparable接口

class Actor implements Comparable<Actor>{
    private String name;
    private int age;
    public Actor(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    @Override
    public String toString() {
        return "Actor{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    //先按姓名排序,姓名相同再按年龄排序
    public int compareTo(Actor o) {
        //name升序 反过来写降序
        int n1=this.getName().compareTo(o.getName());
        //age降序 反过来写升序
        int n2=o.getAge()-this.getAge();
        return n1==0?n2:n1;
    }
}
public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet<Actor> treeSet=new TreeSet<>();
        Actor a1=new Actor("jasper",18);
        Actor a2=new Actor("mike",19);
        Actor a3=new Actor("tom",20);
        Actor a4=new Actor("tom",11);
        treeSet.add(a1);
        treeSet.add(a2);
        treeSet.add(a3);
        treeSet.add(a4);
        System.out.println(treeSet);
        //因为实现了Comparable接口,所以即使添加新的对象,只要比对的属性相同,就无法添加进去
        treeSet.add(new Actor("tom",11));
        System.out.println(treeSet);
        //因为实现了Comparable接口,所以即使删除新的对象,只要比对的属性相同,就能删除
        treeSet.remove(new Actor("mike",19));
        System.out.println(treeSet);

    }
}
//输出结果:
[Actor{name='jasper', age=18}, Actor{name='mike', age=19}, Actor{name='tom', age=20}, Actor{name='tom', age=11}]
[Actor{name='jasper', age=18}, Actor{name='mike', age=19}, Actor{name='tom', age=20}, Actor{name='tom', age=11}]
[Actor{name='jasper', age=18}, Actor{name='tom', age=20}, Actor{name='tom', age=11}]

实现Comparator定制比较

class Waiter{
    private String name;
    private int age;
    public Waiter(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    @Override
    public String toString() {
        return "Waiter{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class TreeSetTest2 {
    public static void main(String[] args) {
        TreeSet<Actor> treeSet=new TreeSet<>(new Comparator<Actor>() {
            @Override
            public int compare(Actor o1, Actor o2) {
                //name升序 反过来写降序
                int n1=o1.getName().compareTo(o2.getName());
                //age降序 反过来写升序
                int n2=o2.getAge()-o1.getAge();
                return n1==0?n2:n1;
            }
        });
        Actor a1=new Actor("jasper",18);
        Actor a2=new Actor("mike",19);
        Actor a3=new Actor("tom",20);
        Actor a4=new Actor("tom",11);
        treeSet.add(a1);
        treeSet.add(a2);
        treeSet.add(a3);
        treeSet.add(a4);
        System.out.println(treeSet);
        treeSet.add(new Actor("tom",11));
        System.out.println(treeSet);
        treeSet.remove(new Actor("mike",19));
        System.out.println(treeSet);
    }
}
//输出结果:
[Actor{name='jasper', age=18}, Actor{name='mike', age=19}, Actor{name='tom', age=20}, Actor{name='tom', age=11}]
[Actor{name='jasper', age=18}, Actor{name='mike', age=19}, Actor{name='tom', age=20}, Actor{name='tom', age=11}]
[Actor{name='jasper', age=18}, Actor{name='tom', age=20}, Actor{name='tom', age=11}]

HashSet和TreeSet的区别

HashSet

  • HashSet内部的数据结构是哈希表(数组+链表+红黑树),是线程不安全的。
  • HashSet集合元素可以是null,但只能放入一个null。
HashSet hashSet=new HashSet();
hashSet.add(null);
hashSet.add(null);//add了两次但只能存一个null
  • HashSet存放对象时,通过对象的hashCode和equals方法来完成对象唯一性的判断。通过hashcode方法找到该对象存放的位置,然后通过equals方法进行比较,如果相同则不添加。
  • HashSet如果同一个类向HashSet添加不同的对象,可以覆盖hashCode方法和equals方法,使其通过比较对象属性值来判断。

TreeSet

  • TreeSet内部的数据结构是红黑树,是线程不安全的。
  • TreeSet集合元素不允许放入null值。
TreeSet treeSet=new TreeSet();
treeSet.add(null);//运行错误 java.lang.NullPointerException
  • TreeSet可以实现Comparable接口,并覆盖其compareTo方法进行排序。TreeSet也可以实现Comparator接口,并覆盖其compare方法进行排序。

  • TreeSet存放对象时,是根据对象的compareTo方法判断两个对象是否相等,并进行比较。

Hi, welcome to JasperのBlog

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值