云计算百科
云计算领域专业知识百科平台

Set集合语法详解

Set集合语法详解

一、Set系列集合

  • 无序 : 存取顺序不一致
  • 不重复 : 可以去除重复
  • 无索引 : 没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

二、Set集合的实现类

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

✏️ 注意
Set接口中的方法上基本与Collection的API一致

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

方法名称说明
public boolean add(E e) 把给定对象添加到当前集合中
public void clear( ) 清空集合中的所有元素
public boolean remove(E e) 把给定的对象在当前集合中删除
public boolean contains(Object obj) 判断当前集合中是否包含给定的对象,底层是通过equals实现的,如果存自定义对象,没有重写equals方法,那么默认使用Object类中的equals方法进行判断,依赖地址值进行判断
public boolean isEmpty( ) 判断当前集合是否为空
public int size( ) 返回集合中元素的个数/集合的长度

⚠️ Set的遍历
1.增强for

2.迭代器

3.Lambda表达式

Set<String> set = new HashSet<>();
set.add("甜甜");
set.add("源源");
set.add("名侦探柯南");
set.add("怪盗基德");

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

// 2.迭代器
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()){
String s = iterator.next();
System.out.println(s);
}

// 3.Lambda表达式
set.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});

set.forEach(s -> System.out.println(s));

1. HashSet

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

💡 哈希表的组成
JDK8之前 : 数组 + 链表

JDK8开始 : 数组 + 链表 + 红黑树

int index = (数组长度 – 1) & 哈希值 —>计算位置

哈希值 :

  • 根据hashCode方法计算出来的int类型的整数
  • 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
  • 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值

对象的哈希值特点:

  • 如果没有重写hashCode方法,不同对象计算出来的哈希值是不同的
  • 如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
  • 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)
1.1.1 HashCode的代码演示

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

public Student() {
}

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

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

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

Student s1 = new Student("甜甜",18);
Student s2 = new Student("甜甜",18);

System.out.println(s1.hashCode()); //29741139
System.out.println(s2.hashCode()); //29741139

//哈希碰撞,小概率事件
System.out.println("abc".hashCode()); //96354
System.out.println("acD".hashCode()); //96354

1.2 HashSet 底层原理 (hashSet.html 原理演示网页,点击学习吧!)
  • 创建一个默认长度为16,默认加载因子为0.75,数组名为table
  • 根据元素的哈希值跟数组的长度计算出应存入的位置
  • 判断当前位置是否为null,如果为null直接存入
  • 如果位置不为null,表示有元素,则调用equals方法依次比较属性值
    • 一样 : 不存(重复元素)

    • 不一样 : 存入数组,形成链表

      JDK8以前 : 新元素存入数组,老元素挂在新元素下面

      JDK8以后 : 新元素直接挂在老元素下面,当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树

  • ⚠️ 注意
    如果集合中存储的是自定义对象,必须要重写hashCode和equals方法

    除自定义对象外,Java底层已经实现了 String Integer

    遍历时按照hash数组从0索引开始遍历,有链表就把链表遍历完,再遍历下一个索引,循环往复

    1.3 LinkedHashSet底层原理(LinkedHashSet.html 原理演示网页,点击学习吧!)
    • 有序、不重复、无索引
    • 这里的有序指的是保证存储和取出的元素顺序一致
    • 原理 : 底层数据结构依然是哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序

    public static void main(String[] args) {

    Student s1 = new Student("甜甜",18);
    Student s2 = new Student("柯南",19);
    Student s3 = new Student("源源",18);
    Student s4 = new Student("灰原哀",18);
    Student s5 = new Student("元太",18);
    Student s6 = new Student("甜甜",18);

    LinkedHashSet<Student> lhs = new LinkedHashSet<>();

    System.out.println(lhs.add(s1));//ture
    System.out.println(lhs.add(s2));//ture
    System.out.println(lhs.add(s3));//ture
    System.out.println(lhs.add(s4));//ture
    System.out.println(lhs.add(s5));//ture
    System.out.println(lhs.add(s6));//false

    System.out.println(lhs);//[Student{name='甜甜', age=18}, Student{name='柯南', age=19}, Student{name='源源', age=18}, Student{name='灰原哀', age=18}, Student{name='元太', age=18}]

    }

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

    💡 TreeSet集合默认的规则

    • 对于数值类型 : Integer、Double,默认按照从小到大的顺序进行排序
    • 对于字符、字符串类型 : 按照字符在ASCII表中的数字升序进行排序

    1.4.2 第一种排序规则(默认排序/自然排序 : Javabean类实现Comparable接口指定比较规则)

    public class Student implements Comparable<Student>{
    @Override
    public int compareTo(Student o) {
    //指定排序的规则
    //只看年龄,我想要按照年龄的升序进行排列
    return this.getAge() o.getAge();
    }
    }

    💡 提示
    this : 表示当前要添加的元素

    o : 表示已经在红黑树存在的元素

    返回值 :

    • 负数 : 认为要添加的元素是小的,存在左边
    • 整数 : 认为要添加的元素是大的,存在右边
    • 0 : 认为要添加的元素已经存在,舍弃
    1.4.3 第二种排序规则(比较器排序 : 创建TreeSet对象的时候,传递比较器Comparator指定规则)

    使用规则 : 默认使用第一种,如果第一种不能满足当前需求,就使用第二种

    • 存入四个字符串, “c” , “ab” , “df” , “qwer” ,按照长度排序,如果一样则按照首字母排序

    public static void main(String[] args) {
    TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
    // o1:表示当前要添加的元素
    // o2:表示已经在红黑树存在的元素
    //返回值规则和之前一样
    //按照长度排序
    int i = o1.length() o2.length();
    //如果一样再按照首字母排序
    i = i == 0 ? o1.compareTo(o2) : i;
    return i;
    }
    });
    //Lambda表达式
    TreeSet<String> ts = new TreeSet<>((o1,o2) -> {
    //按照长度排序
    int i = o1.length() o2.length();
    //如果一样再按照首字母排序
    i = i == 0 ? o1.compareTo(o2) : i;
    return i;
    });

    ts.add("c");
    ts.add("ab");
    ts.add("df");
    ts.add("qwer");

    System.out.println(ts); //输出:a,ab,df,qwer
    }

    三、总结

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

    用ArrayList集合,基于数组的

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

    用LinkedList集合,基于链表的

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

    用HashSet集合,基于哈希表的

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

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

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

    用TreeSet集合,基于红黑树,后续也可以使用List集合实现排序

  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » Set集合语法详解
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!