Java基础 | 集合(四)

简介: Java集合

(四)Set集合

1.Set集合概述和特点

Set集合特点

  • 不包含重复元素的集合
  • 没有带索引的方法,所以不能使用普通for循环

Set集合练习

  • 存储字符串并遍历
import java.util.HashSet;
import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        //创建集合对象
        Set<String> set = new HashSet<>();//HashSet对集合的迭代顺序不作任何保证

        //添加元素
        set.add("hello");
        set.add("java");
        set.add("world");
        //不包含重复元素
        set.add("world");//结果只有一个world

        //遍历集合
        for (String s : set) {
            System.out.println(s);
        }
    }
}

2.哈希值

哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

Object类中有一个方法可以获取对象的哈希值

  • public int hashCode():返回对象的哈希码值

对象的哈希值特点

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
  • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同

3.HashSet集合概述和特点

HashSet集合特点

  • 底层数据结构是哈希表
  • 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以是不包含重复元素的集合

HashSet集合练习

  • 存储字符串并遍历
import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo {
    public static void main(String[] args) {
        HashSet<String> sh = new HashSet<>();
        sh.add("hello");
        sh.add("world");
        sh.add("java");

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

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

    }
}

4.HashSet集合保证元素唯一性源码分析

HashSet集合添加一个元素过程:

image-20220101163525259

HashSet集合存储元素:

  • 要保证元素唯一性,需要重写hashCode()和equals()

5.案例(HashSet集合存储学生对象并遍历)

要求:学生对象的成员变量值相同,我们就认为是同一个对象

代码实现:

创建学生类(需要重写hashCode()和equals()方法来保证元素的唯一性)

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;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    //重写重写hashCode()和equals()方法来保证元素的唯一性
    @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);
    }
}

创建测试类

import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo {
    public static void main(String[] args) {
        HashSet<Student> sh = new HashSet<>();
        Student s1 = new Student("学生1", 20);
        Student s2 = new Student("学生2", 21);
        Student s3 = new Student("学生3", 22);
        Student s4 = new Student("学生1",20);//与s1相同

        sh.add(s1);
        sh.add(s2);
        sh.add(s3);
        sh.add(s4);//结果并未显示s4,因为与s1位同一个元素,在学生类定义中重写了hashCode()和equals()方法确保了元素的唯一性

        //迭代器遍历集合
        Iterator<Student> it = sh.iterator();
        while (it.hasNext()) {
            Student s = it.next();
            System.out.println(s.getName() + ", " + s.getAge());
        }

        //增强for遍历集合
        for(Student s : sh) {
            System.out.println(s.getName() + ", " + s.getAge());
        }
    }
}

6.LinkedHashSet集合概述和特点

LinkedHashSet集合特点

  • 哈希表和链表实现的Set接口,具有可预测的迭代次序
  • 由链表保证元素有序,也就是 说元素的存储和取出顺序是一致的
  • 由哈希表保证元素唯一,也就是说没有重复的元素

LinkedHashSet集合练习

  • 存储字符串并遍历
import java.util.LinkedHashSet;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        //LinkedHashSet集合保证了元素的唯一性也保证了存储和取出的顺序的一致性
        LinkedHashSet<String> lh = new LinkedHashSet<>();
        lh.add("hello");
        lh.add("java");
        lh.add("world");

        lh.add("hello");//元素唯一性,所以结果不显示多余的hello字符串
        for (String s : lh) {
            System.out.println(s);
        }
    }
}

7.TreeSet集合概述和特点

TreeSet集合特点

  • 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法

    ​ TreeSet():根据其元素的自然排序进行排序

    ​ TreeSet(Comparetor comparator):根据指定的比较器进行排序

  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以不包含重复元素

TreeSet集合练习

  • 存储整数并遍历
import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<Integer> ts = new TreeSet<>();
        //添加元素
        ts.add(2);
        ts.add(3);
        ts.add(1);

        //因为它是Set集合,所以不包含重复元素
        ts.add(2);

        for(Integer it: ts) {
            System.out.print(it);//结果为 1 2 3
        }
    }
}

8.自然排序Comparable的使用

  • 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
  • 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写

练习:

  • 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
  • 要求:按照年龄从小到大牌,年龄相同时,按照姓名的字母顺序排序

代码实现:

定义学生类:

public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student() {
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

    @Override
    public int compareTo(Student s) {
//        return -1;//降序
//        return 0;//元素重复
//        return 1;//升序

        int flag = this.age - s.age;    //this在前为升序,在后为降序
        int flag2 = flag == 0 ? this.name.compareTo(s.name) : flag;        //String类自带compareTo方法
        return flag2;
    }
}

创建测试类:

import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>();
        Student s1 = new Student("学生1",22);
        Student s2 = new Student("学生2",23);
        Student s3 = new Student("学生3",33);

        Student s4 = new Student("学生4",33);

        Student s5 = new Student("学生4",33);
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);//s5与s4元素重复所以结果不显示

        for(Student s:ts) {
            System.out.println(s.getName()+", "+s.getAge());
        }
    }
}

9.比较器排序Comparator的使用

  • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
  • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要和次要条件来写

练习:

  • 存储学生对象并遍历,创建TreeSet集合使用带参构造方法
  • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

代码实现

定义一个标准的学生类:(此处省略)

定义测试类:

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

public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int flag = s1.getAge() - s2.getAge();
                int flag2 = flag == 0 ? s1.getName().compareTo(s2.getName()) : flag;
                return flag2;
            }
        });

        Student s1 = new Student("学生1",22);
        Student s2 = new Student("学生2",23);
        Student s3 = new Student("学生3",33);

        Student s4 = new Student("学生4",33);

        Student s5 = new Student("学生4",33);
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);//s5与s4元素重复所以结果不显示

        for(Student s:ts) {
            System.out.println(s.getName()+", "+s.getAge());
        }
    }
}

分析:

这里o1表示位于前面的对象,o2表示后面的对象

  • 返回-1(或负数),表示不需要交换01和02的位置,o1排在o2前面,asc
  • 返回1(或正数),表示需要交换01和02的位置,o1排在o2后面,desc

下面来用我们之前的结论解释为什么 return o2.a - o1.a 就是降序了:

首先o2是第二个元素,o1是第一个元素。无非就以下这些情况:
①: o2.a > o1.a: 那么此时返回正数,表示需要调整o1,o2的顺序,也就是需要把o2放到o1前面,这不就是降序了么。

②:o2.a < o1.a : 那么此时返回负数,表示不需要调整,也就是此时o1 比 o2大, 不还是降序么。


10.案例(不重复的随机数)

编写一个程序,获取10个1~20之间的随机数,要求随机数不能重复,并在控制台输出

import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;

public class SetDemo {
    public static void main(String[] args) {
        //创建集合对象
        //Set<Integer> set = new HashSet<>();//HashSet集合是无序且元素唯一
        Set<Integer> set = new TreeSet<>();//TreeSet集合是有序且元素唯一

        //创建随机数对象
        Random r = new Random();

        //判断集合的长度是否小于10
        while (set.size() < 10) {
            int sum = r.nextInt(20) + 1;
            set.add(sum);
        }

        for(Integer i :set) {
            System.out.println(i);
        }
    }
}
目录
相关文章
|
1月前
|
安全 Java 容器
【Java集合类面试二十七】、谈谈CopyOnWriteArrayList的原理
CopyOnWriteArrayList是一种线程安全的ArrayList,通过在写操作时复制新数组来保证线程安全,适用于读多写少的场景,但可能因内存占用和无法保证实时性而有性能问题。
|
1月前
|
存储 安全 Java
【Java集合类面试二十五】、有哪些线程安全的List?
线程安全的List包括Vector、Collections.SynchronizedList和CopyOnWriteArrayList,其中CopyOnWriteArrayList通过复制底层数组实现写操作,提供了最优的线程安全性能。
|
1月前
|
Java
【Java集合类面试二十八】、说一说TreeSet和HashSet的区别
HashSet基于哈希表实现,无序且可以有一个null元素;TreeSet基于红黑树实现,支持排序,不允许null元素。
|
1月前
|
Java
【Java集合类面试二十三】、List和Set有什么区别?
List和Set的主要区别在于List是一个有序且允许元素重复的集合,而Set是一个无序且元素不重复的集合。
|
1月前
|
Java
【Java集合类面试二十六】、介绍一下ArrayList的数据结构?
ArrayList是基于可动态扩展的数组实现的,支持快速随机访问,但在插入和删除操作时可能需要数组复制而性能较差。
|
1月前
|
存储 Java 索引
【Java集合类面试二十四】、ArrayList和LinkedList有什么区别?
ArrayList基于动态数组实现,支持快速随机访问;LinkedList基于双向链表实现,插入和删除操作更高效,但占用更多内存。
|
14天前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
3天前
|
存储 安全 Java
Java 常用集合分类
Java 常用集合分类
13 2
|
29天前
|
Java
用JAVA架建List集合为树形结构的代码方法
这段代码定义了一个表示树形结构的 `Node` 类和一个用于构建树形结构的 `TreeController`。`Node` 类包含基本属性如 `id`、`pid`、`name` 和 `type`,以及子节点列表 `children`。`TreeController` 包含初始化节点列表并将其转换为树形结构的方法。通过过滤和分组操作实现树形结构的构建。详情可见:[代码示例链接1](http://www.zidongmutanji.com/zsjx/43551.html),[代码效果参考链接2](https://www.257342.com/sitemap/post.html)。
31 5
|
29天前
|
存储 Java 程序员
Java中的集合框架:从入门到精通
【8月更文挑战第30天】在Java的世界里,集合框架是一块基石,它不仅承载着数据的存储和操作,还体现了面向对象编程的精髓。本篇文章将带你遨游Java集合框架的海洋,从基础概念到高级应用,一步步揭示它的奥秘。你将学会如何选择合适的集合类型,掌握集合的遍历技巧,以及理解集合框架背后的设计哲学。让我们一起探索这个强大工具,解锁数据结构的新视角。