【JAVA学习之路 | 进阶篇】Set及其实现类与常用方法

简介: 【JAVA学习之路 | 进阶篇】Set及其实现类与常用方法

1.Set及其常用实现类

Set接口是java.util.Collection接口的子接口.用来存储一个一个的数据.后面学习到的Map接口则用来存储key-value键值对.

Set : 存储无序的,不可重复的数据
    |----->HashSet : 主要实现类 : 底层使用的是HashMap,即使用数组+单向链表+红黑树来存储。
        |----->LinkedSet : 是HashSet的子类,在其存储结构的基础上,增加了一组双向链表,用来记录添加                        
                           元素的先后顺序。即我们可以按照添加的顺序实现遍历.便于频繁查询工作。
    |----->TreeSet : 底层使用红黑树进行存储,可以按照添加的元素的指定属性的大小顺序来进行遍历.

2.Set中常用方法

即Collection接口中的15个抽象方法.没有新增的方法.

3.Set中无序性与不可重复性的理解

(1). 无序性 :并不等同于随机性.

添加元素的顺序与遍历元素的顺序不一致,是否能说明Set的无序性呢?其实是不可以的.因为可以看到,多次运行,遍历结果还是第一次的遍历元素的顺序.

@Test
    public void Test1() {
        Set set = new HashSet();
        //自动装箱
        set.add(12);
        set.add(34);
        set.add("hexua");
        set.add('a');
        //foreach循环
        for (Object obj : set) {
            System.out.println(obj);
        }
    }
 
控制台
//多次运行,都是这个顺序
a
34
12
hexua

无序性与添加的元素的位置有关.其并不像ArrayList是连续排列的.其是根据哈希函数算得的哈希值计算其在数组中的存储位置,因为该位置不是依次紧密排列的,表现为无序性.(学过数据结构的应该觉得比较好理解一点)


(2). 不可重复性 : 添加到Set元素中的元素不能相同.


其比较的标准是hashCode()得到的哈希值及equals()得到的boolean类型的结果.只有哈希值相等,而且equals()返回true,则判断元素已重复,所以不将该元素添加到Set中.


(3). 注 : 添加到HashSet/LinkedHashSet的元素的要求 : 必须重写Object类中的HashCode与equals方法.且二者需要保持一致性.

4.HashSet的底层结构

通过查看源码,发现HashSet的底层是通过HashMap来实现的.

public HashSet() {
        map = new HashMap<>();
    }

而HashMap的底层使用数组+单向链表+红黑树来存储.


例如add(12),通过hashCode方法可以算出一个哈希值,并将该元素放到该哈希值对应的位置存储.该位置存储的是元素的地址.接着后面add(23),add(34)...如果hashCode()算得某个位置,并且该位置已经存储了某个元素的地址(哈希冲突),那么第一个元素可以存储第二个元素的引用,从而构成了单链表.


重复性判断可以这样理解.hashCode()算出一个哈希值,找到该哈希值对应的位置,将该元素(A)与该位置对应的元素(B)(只有单元素情况)进行equals比较.如果相等,则返回,不加入到HashSet中,如果不相等,则原来的元素(B)存储该元素(A)的引用,即加入到HashSet中.如果哈希值对应的位置指向的一条单向链表,则将该元素与指向的一条单链表依次比较.

5.TreeSet的使用

(1). 查看源码发现,TreeSet底层是通过TreeMap实现,故TreeSet底层使用红黑树进行存储.


(2). 可以按照添加元素的指定的属性大小的顺序进行遍历.


(3). 要求 : 添加到TreeSet中的元素对象必须是同一类型,否则会报错.添加的元素需要考虑排序.自然排序or定制排序.即实现Comparable接口重写CompareTo方法或者实现Comparator接口重写Compare方法.


(4). 判断数据是否相等的标准.

  • 由于TreeSet并不是由数组+单向链表进行存储,所以不必重写hashCode与equals方法.
  • 标准考虑自然排序或者定制排序.由于TreeSet中不能存在相同的元素,所以后一个相同的元素不能被添加到TreeSet中.

自然排序(Comparable接口实现compareTo方法)以名字作为比较的依据.

public class Person implements Comparable{
    private String name;
    private int age;
 
    public Person() {
    }
 
    public Person(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;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
 
    @Override
    public int compareTo(Object o) {
        if (o instanceof Person) {
            Person p = (Person) o;
            return this.getName().compareTo(p.getName());
        }
        throw new RuntimeException("异常");
    }
}
 
@Test
    public void Test2() {
        Set set = new TreeSet();
        set.add(new Person("mahuateng", 50));
        set.add(new Person("dinglei", 51));
        set.add(new Person("liuqiangdong", 49));
        set.add(new Person("mayun", 52));
        for (Object obj : set) {
            //默认调用toString方法
            System.out.println(obj);
        }
    }
 
控制台
Person{name='dinglei', age=51}
Person{name='liuqiangdong', age=49}
Person{name='mahuateng', age=50}
Person{name='mayun', age=52}

定制排序(Comparator接口实现Compare方法)以名字作为比较的依据.

按住Ctrl+p,查看到可以放比较器实参.

@Test
    public void Test2() {
        Set set = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Person && o2 instanceof Person) {
                    Person p1 = (Person) o1;
                    Person p2 = (Person) o2;
                    return p1.getAge() - p2.getAge();
                }
                throw new RuntimeException();
            }
        });
        set.add(new Person("mahuateng", 50));
        set.add(new Person("dinglei", 51));
        set.add(new Person("liuqiangdong", 49));
        set.add(new Person("mayun", 52));
        for (Object obj : set) {
            System.out.println(obj);
        }
    }
 
控制台
Person{name='liuqiangdong', age=49}
Person{name='mahuateng', age=50}
Person{name='dinglei', age=51}
Person{name='mayun', age=52}
相关文章
|
26天前
|
存储 Oracle Java
java零基础学习者入门课程
本课程为Java零基础入门教程,涵盖环境搭建、变量、运算符、条件循环、数组及面向对象基础,每讲配示例代码与实践建议,助你循序渐进掌握核心知识,轻松迈入Java编程世界。
223 0
|
1月前
|
IDE Java 编译器
java编程最基础学习
Java入门需掌握:环境搭建、基础语法、面向对象、数组集合与异常处理。通过实践编写简单程序,逐步深入学习,打牢编程基础。
169 0
|
1月前
|
Java
Java语言实现字母大小写转换的方法
Java提供了多种灵活的方法来处理字符串中的字母大小写转换。根据具体需求,可以选择适合的方法来实现。在大多数情况下,使用 String类或 Character类的方法已经足够。但是,在需要更复杂的逻辑或处理非常规字符集时,可以通过字符流或手动遍历字符串来实现更精细的控制。
212 18
|
1月前
|
负载均衡 Java API
grpc-java 架构学习指南
本指南系统解析 grpc-java 架构,涵盖分层设计、核心流程与源码结构,结合实战路径与调试技巧,助你从入门到精通,掌握高性能 RPC 开发精髓。
183 8
|
1月前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
182 4
|
1月前
|
存储 JavaScript Java
(Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
dict字典 Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 我们可以通过声明JS对象一样的方式声明dict
137 1
|
4月前
|
存储 缓存 JavaScript
Set和Map有什么区别?
Set和Map有什么区别?
366 1
|
29天前
|
存储 算法 容器
set_map的实现+set/map加持秒杀高频算法题锻炼算法思维
`set`基于红黑树实现,支持有序存储、自动去重,增删查效率为O(logN)。通过仿函数可自定义排序规则,配合空间配置器灵活管理内存。不支持修改元素值,迭代器失效需注意。`multiset`允许重复元素。常用于去重、排序及查找场景。
|
5月前
|
存储 JavaScript 前端开发
for...of循环在遍历Set和Map时的注意事项有哪些?
for...of循环在遍历Set和Map时的注意事项有哪些?
308 121
|
8月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
227 2