Java中的set集合

Collection

Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。

方法名称说明
public boolean add(E e)把给定的对象添加到当前集合中
public void clear()清空集合中所有的元素
public boolean remove(E e)把给定的对象在当前集合中删除
public boolean contains(Object obj)判断当前集合中是否包含给定的对象
public boolean isEmpty()判断当前集合是否为空
public int size()返回集合中元素的个数/集合的长度

Set系列集合

  • 无序:存储顺序不一致
  • 不重复:可以去除重复
  • 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素
  • Set集合的方法基本上与Collection的API一致
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class A01_SetDemo1 {
    public static void main(String[] args) {
        /*
        利用Set系列的集合,添加字符串,并使用多种方式遍历
        迭代器
        增强for
        lambda表达式
         */

        // 1. 创建一个Set集合的对象
        Set<String> s = new HashSet<>();
        // 2. 添加元素
        // 如果当前元素是第一次添加,则可以添加成功,返回true;否则,添加失败,返回false
        boolean r1 = s.add("张三");
        boolean r2 = s.add("张三");
        System.out.println(r1);  // true
        System.out.println(r2);  // false
        s.add("李四");
        s.add("王五");

        // 3. 打印集合
        // 无序
        System.out.println(s);  // [李四, 张三, 王五]

        // 迭代器遍历
        Iterator<String> it = s.iterator();
        while(it.hasNext()) {
            String str = it.next();
            System.out.println(str);
        }

        // 增强for
        for (String str : s) {
            System.out.println(str);
        }

        // lambda表达式
        s.forEach(str-> System.out.println(str));
    }
}

Set集合的实现类

  • HashSet:无序、不重复、无索引
  • LinkedHashSet:有序、不重复、无索引
  • TreeSet:可排序、不重复、无索引

HashSet

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

HashSet底层原理:

  • HashSet集合底层采取哈希表存储数据
  • 哈希表是一种对于增删改查数据性能都较好的结构

哈希表的组成:

  • JDK8之前:数组 + 链表
  • JDK8开始:数组 + 链表 + 红黑树
    ①创建一个默认长度16,默认加载因子为0.75的数组,数组名为table。
          当数组存了16 * 0.75 = 12个元素的时候,数组会扩容为原来的两倍。
          当链表长度大于8而且数组长度大于等于64时,链表会自动转换为红黑树,以提高查找效率。
  • ②根据元素的哈希值跟数组的长度计算出应存入的位置:
                                    int index = (数组长度 - 1) & 哈希值
    ③判断当前位置是否为null,表示有元素,则调用equals方法比较属性值
    ④如果位置不为null,表示有元素,则调用equals方法比较属性值
    ⑤属性值一样:不存;属性值不一样:存入数组,形成链表
         JDK8以前:新元素存入数组,老元素挂在新元素下面
         JDK8以后:新元素直接挂在老元素下面
     

哈希值:

  • 根据hashCode方法计算出来的int类型的整数
  • 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
    如果集合中存储的是自定义对象,必须重写hashCode和equals方法
  • 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
  • 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
  • 如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
  • 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)。

// Student类


import java.util.Objects;

public class Student {
    private String name;
    private int age;


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

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

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

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

// 测试类

public class A02_HashSetDemo1 {
    public static void main(String[] args) {
        /*
        哈希值:
            对象的整数表现形式
            1. 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
            2. 如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
            3. 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)。
         */

        // 1. 创建对象
        Student s1 = new Student("zhangsan", 23);
        Student s2 = new Student("zhangsan", 23);

        // 2. 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
        //    如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
        System.out.println(s1.hashCode());  // -1461067292
        System.out.println(s2.hashCode());  // -1461067292

        // 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)。
        System.out.println("abc".hashCode());  // 96354
        System.out.println("acD".hashCode());  // 96354
    }
}

LinkedHashSet

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

LinkedHashSet底层原理:

底层数据结构依然是哈希表,只是每个元素额外的多了一个双向链表的机制记录存储的顺序。

import java.util.LinkedHashSet;

public class A04_LinkedHashSetDemo {
    public static void main(String[] args) {
        // 1. 创建4个学生对象
        Student s1 = new Student("zhangsan", 23);
        Student s2 = new Student("lisi", 24);
        Student s3 = new Student("wangwu", 25);
        Student s4 = new Student("zhangsan", 23);

        // 2. 创建集合的对象
        LinkedHashSet<Student> lhs = new LinkedHashSet<>();

        // 3. 添加元素
        System.out.println(lhs.add(s1));  // true
        System.out.println(lhs.add(s2));  // true
        System.out.println(lhs.add(s3));  // true
        System.out.println(lhs.add(s4));  // false

        // 4. 打印集合
        System.out.println(lhs);
        // [Student{name = zhangsan, age = 23}, Student{name = lisi, age = 24}, Student{name = wangwu, age = 25}]
    }
}

TreeSet

特点:

  • 可排序、不重复、无索引
  • 可排序:按照元素的默认规则(从小到大)排序
  • TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好
import java.util.Iterator;
import java.util.TreeSet;

public class A04_TreeSetDemo1 {
    public static void main(String[] args) {
        /*
        利用TreeSet存储整数并进行排序
         */

        // 1. 创建TreeSet集合对象
        TreeSet<Integer> ts = new TreeSet<>();

        // 添加元素
        ts.add(4);
        ts.add(5);
        ts.add(1);
        ts.add(3);
        ts.add(2);

        // 3. 打印集合
//        System.out.println(ts);  // [1, 2, 3, 4, 5]

        // 4. 遍历集合(三种遍历)
        // 迭代器
/*        Iterator<Integer> it = ts.iterator();
        while (it.hasNext()) {
            int i = it.next();
            System.out.println(i);
        }*/
        
        // 增强for
/*        for (Integer t : ts) {
            System.out.println(t);
        }*/

        // lambda表达式
        ts.forEach(i-> System.out.println(i));
    }
}

TreeSet集合排序默认的规则:Javabean类实现Comparable接口指定比较规则

  • 对于数值类型:Integer、Double,默认按照从小到大的顺序进行排序。
  • 对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序。
// Student类
public class Student implements Comparable<Student>{
    private String name;
    private int age;


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    @Override
    // this:表示当前要添加的元素
    // o:表示已经在红黑树存在的元素

    // 返回值:负数:表示当前要添加的元素是小的,存左边
    // 正数:表示当前要添加的元素是大的,存右边
    // 0:表示当前要添加的元素已经存在,舍弃
    public int compareTo(Student o) {
        // 指定排序的规则
        // 要求按照学生的年龄进行升序排序,同年龄按照名字字母排列(暂不考虑中文)
        int result = this.getAge() - o.getAge();

        return result;
    }
}
// 测试类

import java.util.TreeSet;

public class A04_TreeSetDemo2 {
    public static void main(String[] args) {
        /*
        需求:创建TreeSet集合,并添加3个学生对象
        学生对象属性:姓名、年龄
        要求按照学生的年龄进行排序,同年龄按照名字字母排列(暂不考虑中文)
        同姓名、同年龄认为是同一个人

        方式一:默认的排序规则/自然排序
               Student类实现Comparable接口,重写里面的抽象方法,再指定比较规则

         */

        // 创建3个学生对象
        Student s1 = new Student("zhangsan", 23);
        Student s2 = new Student("lisi", 24);
        Student s3 = new Student("wangwu", 25);

        // 创建集合对象
        TreeSet<Student> ts = new TreeSet<>();

        // 添加元素
        ts.add(s3);
        ts.add(s2);
        ts.add(s1);

        // 打印集合
        System.out.println(ts);

    }
}

TreeSet集合比较器排序:创建TreeSet对象的时候,传递比较器Comparator指定规则

package com.itheima.a05myset;
import java.util.TreeSet;

public class A06_TreeSetDemo3 {
    public static void main(String[] args) {
        /*
        需求:请自行选择比较器排序和自然排序两种方式
        要求:存入四个字符串,”c", "ab", "df", "qwer"
        按照长度排序,如果一样则按照首字母排序

        采取第二中排序方式:比较器排序
         */

        // 1. 创建集合
        // o1:表示当前要添加的元素
        // o2: 表示已经在红黑树存在的元素
        TreeSet<String> ts = new TreeSet<>((o1, o2)->{
                // 按照长度进行排序
                int i = o1.length() - o2.length();
                // 如果长度一样,按照首字母排序
                i = i == 0 ? o1.compareTo(o2) : i;
                return i;
        });

        // 2. 添加元素
        ts.add("c");
        ts.add("ab");
        ts.add("df");
        ts.add("qwer");

        // 3. 打印集合
        System.out.println(ts);  // [c, ab, df, qwer]
    }
}

使用选择:

1. 如果想要集合中的元素可重复

  • 用ArrayList集合,基于数组的。(用的最多)

2. 如果想要集合中的元素可重复,而且当前的增删操作明显多于查询

  • 用LIinkedList集合,基于链表的。

3. 如果想对集合中的元素去重

  • 用HashSet集合,基于哈希表的。(用到最多)

4. 如果想对集合中的元素去重,而且保证存取顺序

  • 用LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet。

5. 如果想对集合中的元素进行排序

  • 用TreeSet集合,基于红黑树。后续也可以用List集合实现排序。
  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值