Java集合(3:Set和Map)

devtools/2024/10/22 10:20:36/

文章目录

  • Set
    • 概述
    • 哈希值
    • HashSet去重原理
    • LinkedHashSet
    • TreeSet
      • 自定义排序规则
  • Map
    • 概述
    • Map的基本方法
    • Map集合的获取功能
    • 哈希表
    • HashMap
      • 底层源码
    • 特点
    • 注意


Set

概述

  • Set集合也是一个接口,继承自Collection,与List类似,都需要通过实现类来进行操作。
    特点:
  1. 不允许包含重复的值
  2. 没有索引(就不能使用普通的for循环进行遍历)
java">import java.util.HashSet;
import java.util.Set;public class Demo01 {public static void main(String[] args) {//使用多态,父类的引用指向子类对象Set<String> set = new HashSet<>();//添加元素set.add("黄固");set.add("欧阳锋");set.add("段智兴");set.add("洪七公");set.add("段智兴");System.out.println(set);//打印集合//[洪七公, 黄固, 欧阳锋, 段智兴]//HashSet集合对于元素的读写顺序不做保证//相同的元素,多次存储,只能保留一个,并且不会报错//List集合可以存储重复元素,Set集合不行}
}

例:双色球

java">import java.util.Random;
import java.util.TreeSet;public class Demo02 {public static void main(String[] args) {Random ran = new Random();//创建随机类对象int blueBall = ran.nextInt(16) + 1;
//        HashSet<Integer> redBalls = new HashSet<>();//创建集合用来存储红球TreeSet<Object> redBalls = new TreeSet<>();//TreeSet集合自带排序规则while (redBalls.size() < 6){redBalls.add(ran.nextInt(33) + 1);//将当前生成的红球直接存进集合中//因为Set集合不能存储重复的元素,所以去重的操作可以省略不做。}System.out.println("红球:" + redBalls + "篮球 [" + blueBall + "]");}
}

哈希值

Set集合的去重原理使用的是哈希值。
哈希值就是JDK根据对象地址 或者 字符串 或者数值 通过自己内部的计算出来的一个整数类型数据

public int hashCode() - 用来获取哈希值,来自于Object顶层类

对象的哈希值特点:

  • 同一个对象多次调用hashCode()方法,得到的结果是相同的。
  • 默认情况下,不同的对象的哈希值也是不同的(特殊情况除外)
java">public class Demo03 {public static void main(String[] args) {//相同对象哈希值相同System.out.println("张三".hashCode());//774889System.out.println("张三".hashCode());//774889//不同对象哈希值不同System.out.println(new Object().hashCode());System.out.println(new Object().hashCode());//不同的对象的哈希值也有可能相同,例外情况System.out.println("辂鹅".hashCode());//1179395System.out.println("较鸦".hashCode());//1179395System.out.println("辄鸇".hashCode());//1179395System.out.println("辅鷨".hashCode());//1179395}
}

HashSet去重原理

HashSet集合的特点:

  1. 底层结构是“哈希表”
  2. 集合对于读写顺序不做保证
  3. 没有索引
  4. Set集合中的内容不能重复
java">public class Demo04 {public static void main(String[] args) {HashSet<Student> set = new HashSet<>();//添加元素set.add(new Student("黄固",28));set.add(new Student("欧阳锋",38));set.add(new Student("段智兴",48));set.add(new Student("洪七公",40));set.add(new Student("段智兴",48));//从程序的角度来考虑,两个段智兴不是同一个对象,都有自己的存储空间,所以哈希值也不一样。for (Student stu : set) {System.out.println(stu);}/*重写hashcode和equalsStudent{name='段智兴', age=48}Student{name='欧阳锋', age=38}Student{name='洪七公', age=40}Student{name='黄固', age=28}*/}
}

LinkedHashSet

特点:

  1. LinkedHashSet是哈希表和链表实现的Set接口,具有可预测的读写顺序。
  2. 有链表来保证元素有序
  3. 有哈希表来保证元素的唯一性
java">public class Demo05 {public static void main(String[] args) {LinkedHashSet<String> set = new LinkedHashSet<>();//添加元素set.add("黄固");set.add("欧阳锋");set.add("段智兴");set.add("洪七公");set.add("段智兴");//重复的元素不能存进去System.out.println(set);//打印集合 [黄固, 欧阳锋, 段智兴, 洪七公]}
}

TreeSet

  1. TreeSet集合底层实际上是一个TreeMap
  2. TreeMap集合底层是一个二叉树。
  3. 放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。
  4. TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序。
java">import java.util.TreeSet;
public class TreeSetTest02 {public static void main(String[] args) {// 创建一个TreeSet集合TreeSet<String> ts = new TreeSet<>();// 添加Stringts.add("zhangsan");ts.add("lisi");ts.add("wangwu");ts.add("zhangsi");ts.add("wangliu");// 遍历for(String s : ts){// 按照字典顺序,升序!System.out.println(s);}/*lisiwangliuwangwuzhangsanzhangsi*/TreeSet<Integer> ts2 = new TreeSet<>();ts2.add(100);ts2.add(200);ts2.add(900);ts2.add(800);ts2.add(600);ts2.add(10);for(Integer elt : ts2){// 升序!System.out.println(elt);}}
}

自定义排序规则

对于自定义的类无法排序,因为类中对象之间没有比较规则,不知道谁大谁小。

java">import java.util.TreeSet;public class TreeSetTest04 {public static void main(String[] args) {Customer c1 = new Customer(32);Customer c2 = new Customer(20);Customer c3 = new Customer(30);Customer c4 = new Customer(25);// 创建TreeSet集合TreeSet<Customer> customers = new TreeSet<>();// 添加元素customers.add(c1);customers.add(c2);customers.add(c3);customers.add(c4);// 遍历for (Customer c : customers){System.out.println(c);}}
}// 放在TreeSet集合中的元素需要实现java.lang.Comparable接口。
// 并且实现compareTo方法。equals可以不写。
class Customer implements Comparable<Customer>{int age;public Customer(int age){this.age = age;}// 需要在这个方法中编写比较的逻辑,或者说比较的规则,按照什么进行比较!// k.compareTo(t.key)// 拿着参数k和集合中的每一个k进行比较,返回值可能是>0 <0 =0// 比较规则最终还是由程序员指定的:例如按照年龄升序。或者按照年龄降序。@Overridepublic int compareTo(Customer c) { // c1.compareTo(c2);return c.age - this.age;}public String toString(){return "Customer[age="+age+"]";}
}

匿名内部类方式

java">public class TreeSetTest05 {public static void main(String[] args) {
//        TreeSet<Student> ts = new TreeSet<>();//默认排序规则TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {int res = o1.getAge() - o2.getAge();return 0 == res ? o1.getName().compareTo(o2.getName()) : res;//三目运算符  等于零用姓名排序}});//默认排序规则//添加元素ts.add(new Student("Andy",19));ts.add(new Student("Jack",18));ts.add(new Student("Tom",21));ts.add(new Student("Lucy",17));ts.add(new Student("Bob",21)); //当年龄相同时,按照姓名的字典顺序排序for (Student stu : ts) {System.out.println(stu);}}
}

Comparable和Comparator怎么选择呢?
当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议实现Comparable接口。
如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。

Map

概述

双列集合:用来存储键值对的集合。

  • interface Map<K,V> : K(key)键 ,V(value)值
  • 将键映射到值的对象,不能出现重复的键,每个键最多可以映射到一个值

1、Map和Collection没有继承关系。
2、Map集合以key和value的方式存储数据:键值对
key和value都是引用数据类型。
key和value都是存储对象的内存地址。
key起到主导的地位,value是key的一个附属品。

Map的基本方法

方法名说明
V put(K key,V value)设置键值对
V remove(Object key)删除元素
void clear()清空集合
boolean containsKey(Object key)判断键是否存在,存在则返回true
boolean containsValue(Object value)判断值是否存在,存在则返回true
boolean isEmpty()判断集合是否为空
int size()获取集合元素个数
java">import java.util.HashMap;
import java.util.Map;public class Map01 {public static void main(String[] args) {Map<String,String> map = new HashMap<>();map.put("STU001","Andy");map.put("STU002","Jack");map.put("STU003","Tom");map.put("STU004","Bob");map.put("STU004","Smith");//设置(修改)//如果键不存在,则表示添加元素。如果键存在,则表示设置值。//删除System.out.println(map.remove("STU003"));  //Tom//判断是否包含System.out.println(map.containsKey("STU003"));  //falseSystem.out.println(map.containsKey("STU004"));  //trueSystem.out.println("-----------------------");System.out.println(map.containsValue("Tom"));  //falseSystem.out.println(map.containsValue("Smith")); //trueSystem.out.println("-----------------------");System.out.println(map.isEmpty());//判断集合是否为空   falsemap.clear();//清空集合System.out.println(map.isEmpty()); //trueSystem.out.println(map); //{}}
}

Map集合的获取功能

java">import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class map_get {public static void main(String[] args) {Map<String,String> map = new HashMap<>();map.put("STU001","Andy");map.put("STU002","Jack");map.put("STU003","Tom");map.put("STU004","Bob");//get通过键获取值System.out.println(map.get("STU003"));System.out.println("------------------");//keySet 获取所有键的Set集合Set<String> keySet = map.keySet();System.out.println(keySet);//values  获取所有值的Collection集合Collection<String> values = map.values();System.out.println(values);//entrySet  获取所有键值对对象的Set集合Set<Map.Entry<String, String>> es = map.entrySet();//Map集合通过entrySet()方法转换成的这个Set集合,Set集合中元素的类型是 Map.Entry<K,V>//Map.Entry和String一样,都是一种类型的名字,只不过:Map.Entry是静态内部类,是Map中的静态内部类System.out.println(es);//[STU001=Andy, STU003=Tom, STU002=Jack, STU004=Bob]for (Map.Entry<String, String> entry:es){System.out.println("key:"+entry.getKey()+"    "+"value:"+entry.getValue());}/*key:STU001    value:Andykey:STU003    value:Tomkey:STU002    value:Jackkey:STU004    value:Bob*/}
}

哈希表

通过 数组 + 链表 实现的一种数据结构

哈希表的构造方法的参数是一个长度为16个元素的数组,通过哈希值 % 16 的值,作为头节点在数组中选择对应的位置,就形成了哈希表。

HashMap

底层源码

java"> public class HashMap{// HashMap底层实际上就是一个数组。(一维数组)Node<K,V>[] table;// 静态的内部类HashMap.Nodestatic class Node<K,V> {final int hash; // 哈希值(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数/算法,可以转换存储成数组的下标。)final K key; // 存储到Map集合中的那个keyV value; // 存储到Map集合中的那个valueNode<K,V> next; // 下一个节点的内存地址。}}

特点

  • 1、无序,不可重复。
    为什么无序? 因为不一定挂到哪个单向链表上。
    不可重复是怎么保证的? equals方法来保证HashMap集合的key不可重复。
    如果key重复了,value会覆盖。

  • 2、放在HashMap集合key部分的元素其实就是放到HashSet集合中了。
    所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法。

  • 3、HashMap集合的默认初始化容量是16,默认加载因子是0.75
    这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组以二叉树开始扩容。

重点,记住:HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,
这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。

注意

1.向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法! equals方法有可能调用,也有可能不调用。 拿put(k,v)举例,什么时候equals不会调用?
k.hashCode()方法返回哈希值, 哈希值经过哈希算法转换成数组下标。 数组下标位置上如果是null,equals不需要执行。 拿get(k)举例,什么时候equals不会调用? k.hashCode()方法返回哈希值, 哈希值经过哈希算法转换成数组下标。 数组下标位置上如果是null,equals不需要执行。
4.假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了 纯单向链表。
这种情况我们成为:散列分布不均匀。什么是散列分布均匀?

假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的, 是散列分布均匀的。假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题? 不行,因为这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。 也是散列分布不均匀。散列分布均匀需要你重写hashCode()方法时有一定的技巧。


http://www.ppmy.cn/devtools/127802.html

相关文章

Electron兼容win7版本的打包流程

Electron 兼容 Windows 7 版本的应用程序的打包流程是一个系统而规范的过程&#xff0c;旨在确保所生成的可执行文件能够在目标操作系统上稳定且高效地运行。以下是该流程的具体步骤&#xff1a; 1、 环境准备&#xff1a; 确保开发环境中已安装Node.js&#xff08;13.14.0&am…

Ansible 自动化运维工具

自动化运维工具&#xff1a; pupetc/s架构&#xff0c;需要安装客户端和服务端saltstackc/s架构&#xff0c;需要安装客户端和服务端&#xff1b;python语言chefc/s架构&#xff0c;需要安装客户端和服务端Ansible不需要安装客户端&#xff0c;ssh连接客户端&#xff1b;pytho…

数据结构(8.3_2)——快速排序

算法思想&#xff1a; 设置两个指针&#xff0c;一个i指针初值为low和一个j指针初值为high&#xff0c;j指针从左往右移&#xff0c;当j指向的元素小于枢轴元素&#xff0c;将该元素放到枢轴元素左边&#xff0c;i指针从右往左移&#xff0c;当i指向的元素大于枢轴元素&#xf…

笔记:WPF中MarkupExtension使用的IServiceProvider参数都有哪些

一、目的&#xff1a;WPF中MarkupExtension使用的IServiceProvider参数都有哪些&#xff0c;都是做什么的 在 WPF 中&#xff0c;MarkupExtension 类的 ProvideValue 方法接受一个 IServiceProvider 参数。IServiceProvider 是一个服务定位器接口&#xff0c;允许你在运行时获取…

高级sql技巧

以下是一些高级 SQL 技巧: 一、窗口函数 窗口函数可以在不影响数据分组的情况下,对数据进行排序、聚合等操作,非常强大。 排名函数 ROW_NUMBER():为每一行分配一个唯一的连续整数序号。RANK():计算排序值,如果有相同的值会出现并列排名,并且下一个排名会跳过相应的数量…

新一代Linux防火墙已经来临(iptables面临淘汰)

本文全面的介绍了iptables和nftables这两个Linux防火墙工具的基本概念及其主要区别&#xff0c;并给出了选择哪一个工具的建议。 iptables是较早版本的Linux防火墙工具&#xff0c;它已经广泛应用于各种Linux发行版中。iptables的优点在于其广泛的文档支持和社区经验积累&…

在MySQL中为啥引入批量键访问(Batch Key Access, BKA)

批量键访问&#xff08;Batch Key Access, BKA&#xff09; 是 MySQL 在某些情况下用于优化 JOIN 操作的一种技术&#xff0c;特别是在通过索引进行 JOIN 时&#xff0c;它能有效减少查询的随机 I/O。批量键访问优化通过将一批主键或索引键一次性发送给存储引擎来查找匹配的行&…

自适应权重

自适应权重&#xff08;adaptive weights&#xff09;是一种动态调整权重的策略&#xff0c;广泛应用于深度学习和机器学习的不同领域。这种策略的核心思想是&#xff0c;在模型训练或推理过程中&#xff0c;根据输入数据、模型状态或任务需求来调整各个部分的权重&#xff0c;…