Java 进阶集合Set、Map(二)

简介: Java 进阶集合Set、Map

一、Set集合

1、HashSet集合

1)原理

底层采用哈希表存储数据

2)组成

jdk8之前:底层数组+链表组成

jdk8之后:底层数组+链表+红黑树组成

3)哈希值

对象的内存地址经过处理后的结构,由于每个对象的内存地址都不一样,所以哈希码也不一样。更重要的是:这里的内存地址是JVM虚拟机虚拟出来的地址,并不是真实的物理内存地址

4)哈希值特点

同一个对象多次调用hashCode()方法返回的哈希值是相同的

默认情况下,不同对象的哈希值是不同的

5)jdk8哈希表流程:

Constructs a new, empty set; the backing HashMap instance has default initial capacity (16) and load factor (0.75).

创建一个新的空集合,默认初始值是16,加载因子(0.75)

根据元素的哈希值和数组的长度计算出应存入的位置

首先判断当前位置是否为NULL,空则直接存入,非空则equals方法比较属性值,相同则不存,不同则存入数组

当数组存储满足16*0.75=12时自动扩容,每次扩容会是当前容量的2倍

d08ba0f00ece82d72bfbbbe476d38ed.png

import java.util.HashSet;
public class ClassStructure {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("aa");
        set.add("bb");
        set.add("cc");
        set.add("aa");
        set.add("ee");
        set.add("ff1");
        set.add("ff2");
        set.add("ff3");
        System.out.println(set);
        //哈希值
        System.out.println(set.hashCode());
    }
}

2、LinkedHashSet集合

1)概述

有序、不重复、无索引

有序指保证存储和取出元素顺序一致

2)原理

底层数据结构哈希表,每个元素增加双链表机制记录数据存储顺序

5b8aed8a9ea8b87fb843eb8aea7be1e.png

f16520370cfd8f03e6252ee5df3f9cc.png

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class ClassStructure {
    public static void main(String[] args) {
        Set set = new LinkedHashSet<>();
        set.add("aa");
        set.add(123);
        set.add(456);
        set.add("hello");
        System.out.println(set);
    }
}

3、TreeSet集合

不重复、无索引、可排序

可排序:按照元素的大小默认升序(有小到大)排序。

TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。

注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序

1)默认规则

对于数值类型:Integer , Double,默认升序排序。

对于字符串类型:默认按照首字符的编号升序排序。

对于自定义类型对象,TreeSet无法直接排序。

2) 自定义规则

TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来定制比较规则。

eef540d8eb5a1d4b121cc4576c0529e.png

public class Student {
    private int id;
    private String name;
    private int age;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    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 "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class ClassStructure {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.setId(1);
        stu.setName("zhangsan");
        stu.setAge(11);
        Student stu1 = new Student();
        stu1.setId(3);
        stu1.setName("zhangsan3");
        stu1.setAge(33);
        Student stu2 = new Student();
        stu2.setId(2);
        stu2.setName("zhangsan2");
        stu2.setAge(22);
        Student stu3 = new Student();
        stu3.setId(5);
        stu3.setName("zhangsan5");
        stu3.setAge(55);
        Set<Student> set = new TreeSet<>(
                new Comparator<Student>() {
                    @Override
                    public int compare(Student o1, Student o2) {
                        return o1.getId()-o2.getId();
                    }
                }
        );
        set.add(stu);
        set.add(stu1);
        set.add(stu2);
        set.add(stu3);
        for (Student id:set){
            System.out.println(id);
        }
    }
}

让自定义的类,实现Comparable接口重写里面的compareTo方法来定制比较规则。

99f6137de347a0b2db25d21fea37094.png

public class Student implements Comparable<Student>{
    private int id;
    private String name;
    private int age;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    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 "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    /*
    默认升序
     */
    @Override
    public int compareTo(Student o) {
        return this.id - o.id;
    }
}
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class ClassStructure {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.setId(1);
        stu.setName("zhangsan");
        stu.setAge(11);
        Student stu1 = new Student();
        stu1.setId(3);
        stu1.setName("zhangsan3");
        stu1.setAge(33);
        Student stu2 = new Student();
        stu2.setId(2);
        stu2.setName("zhangsan2");
        stu2.setAge(22);
        Student stu3 = new Student();
        stu3.setId(5);
        stu3.setName("zhangsan5");
        stu3.setAge(55);
        Set<Student> set = new TreeSet<>();
        set.add(stu);
        set.add(stu1);
        set.add(stu2);
        set.add(stu3);
        for (Student id:set){
            System.out.println(id);
        }
    }
}

4、可变参数(动参)

定义

可变参数用在形参中可以接收多个数据。

可变参数的格式:数据类型…参数名称

作用

接收参数非常灵活,方便。可以不接收参数,可以接收1个或者多个参数,也可以接收一个数组

可变参数在方法内部本质上就是一个数组。

注意

一个形参列表中可变参数只能有一个

可变参数必须放在形参列表的最后面

题目(实战),需求:

假如需要定义一个方法求和,该方法可以灵活的完成如下需求:

计算1个数据的和。

计算2个数据的和。

计算3个数据的和。

计算n个数据的和,甚至可以支持不接收参数进行调用。

7279934d79e860112a487a469344ae8.png

public class ClassSructure {
    public static void Test(Integer... args) {
        int sum = 0;
        for (int ag : args) {
            sum += ag;
        }
        System.out.println(sum);
    }
    public static void main(String[] args) {
        Test(1);
        Test(1, 2);
        Test(1, 2, 3);
    }
}

二、Map集合

1、概述

Map是双列集合,每个元素包含两个数据,格式(Key=Value),也称为键值对集合

2、特点

Map集合的键是无序,不重复的,无索引的,值不做要求(可以重复)

Map集合后面重复的键对应的值会覆盖前面重复键的值,键值对都可以为null。

3、常用API

方法名 解释

V put(K key,V value) 添加元素

V remove(Object key) 根据键删除键值对元素

void clear() 清空所有的键值对元素

int size() 集合的长度,集合中键值对的个数

boolean containsKey(Object key) 判断集合是否包含指定的键

boolean containsValue(Object value) 判断集合是否包含指定的值

boolean isEmpty() 判断集合是否为空

4、HashMap 用的最多

元素按照键是无序,不重复,无索引,值不做要求(可以重复)

-实战根据key求value

038603882c73357e7416247870f2711.png

import java.util.HashMap;
import java.util.Map;
public class ClassStructure {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"数学");
        map.put(2,"语文");
        map.put(3,"英语");
        map.put(4,"物理");
        map.put(5,"政治");
        String str = map.get(1);
        System.out.println(str);
        String str1 = map.get(3);
        System.out.println(str1);
    }
}

5、LinkedHashMap

元素按照键是有序,不重复,无索引,值不做要求

实战 判断key是否存在

f0669cc8cc59d8a55ee4142620c0af5.png

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class ClassStructure {
    public static void main(String[] args) {
        Map<Integer,String> map = new LinkedHashMap<>();
        map.put(1,"数学");
        map.put(2,"语文");
        map.put(3,"英语");
        map.put(4,"物理");
        map.put(5,"政治");
        boolean str = map.containsKey(1);
        System.out.println(str);
        boolean str1 = map.containsKey(6);
        System.out.println(str1);
    }
}

6、TreeMap

元素按照建是排序,不重复,无索引的,值不做要求

实战 参考TreeSet

Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据


相关文章
|
4天前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
4天前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
|
4天前
|
Java 开发者
|
3天前
|
存储 Java 开发者
Java中的集合框架深入解析
【10月更文挑战第32天】本文旨在为读者揭开Java集合框架的神秘面纱,通过深入浅出的方式介绍其内部结构与运作机制。我们将从集合框架的设计哲学出发,探讨其如何影响我们的编程实践,并配以代码示例,展示如何在真实场景中应用这些知识。无论你是Java新手还是资深开发者,这篇文章都将为你提供新的视角和实用技巧。
6 0
|
Java
java中Set,Map,Stack一些简单用法
1 import java.util.Iterator; 2 import java.util.Stack; 3 import java.io.*; 4 import java.util.
632 0
|
10天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
1天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
1天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
15 1
|
4天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####