22 JAVA 4-2 Set HashSet Map HashMap

本文深入探讨了Java中的Map接口,包括其特点、常用方法以及实际运用。Map接口不允许键重复,键值对无序,初始容量为16,加载因子为0.75。HashMap作为实现Map接口的例子,详细解析了其内部结构和扩容机制。通过实例展示了HashMap如何存储数据,并提供了字符串中字符统计的练习。此外,文章还提及了Set接口和HashSet,强调了Set集合中数据的唯一性和无序性。
摘要由CSDN通过智能技术生成

1 Map接口

1.1 概述

Java.util接口Map<K,V>
类型参数 : K - 表示此映射所维护的键 V – 表示此映射所维护的对应的值
也叫做哈希表、散列表. 常用于键值对结构的数据.其中键不能重复,值可以重复
在这里插入图片描述

1.2 特点

  1. Map可以根据键来提取对应的值
  2. Map的键不允许重复,如果重复,对应的值会被覆盖
  3. Map存放的都是无序的数据
  4. Map的初始容量是16,默认的加载因子是0.75

在这里插入图片描述

TIPS:源码摘抄:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
初始容量1<<4,相当于1
(2^4),也就是16
static final float DEFAULT_LOAD_FACTOR = 0.75f;
默认的加载因子是0.75f,也就是存到75%开始扩容,按照2的次幂进行扩容
*

1.3 继承结构

在这里插入图片描述

1.4 常用方法

学习Map接口中的方法即可

void clear() 从此映射中移除所有映射关系(可选操作) boolean containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true
boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图
boolean equals(Object o) 比较指定的对象与此映射是否相等
V get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null
int hashCode() 返回此映射的哈希码值
boolean isEmpty() 如果此映射未包含键-值映射关系,则返回 true
Set keySet() 返回此映射中包含的键的 Set 视图
V put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)
void putAll(Map<? extends K,? extends V> m)从指定映射中将所有映射关系复制到此映射中(可选操作)
V remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)
int size() 返回此映射中的键-值映射关系数
Collection values() 返回此映射中包含的值的 Collection 视图

1.5 练习:Map常用方法测试

package cn.collection;

import java.util.*;
/* 本类用于测试map接口*/
public class TestMap {
    public static void main(String[] args) {
        //1.创建MAP对象
        Map<Integer,String> map = new HashMap();
        map.put(9527,"白骨精");
        map.put(9528,"黑熊精");
        map.put(9529,"鲤鱼精");
        map.put(9530,"黄毛精");
        map.put(9531,"黑熊精");
        map.put(9527,"女儿国国王");
        System.out.println(map);
        /*
        1.map中存放的都是无序的数据,
        2.map中的value可以重复,比如我们放入了俩个黑熊精
        3.map中的key不能重复,如果重复,新的会将旧的覆盖掉
        */

        //3.进行方法测试
        //map.clear();//清空集合
        System.out.println(map.hashCode());//474360095 获取集合的哈希码
        System.out.println(map.equals("黄毛精"));//false  判断“黄毛怪”是否与集合对象相等
        System.out.println(map.isEmpty());//false  判断集合是否为空
        System.out.println(map.size());//5  获取集合中键值对的个数

        System.out.println(map.containsKey(9527));//true 判断当前map集合中是否包含指定的Key键
        System.out.println(map.containsValue("黑熊精"));//true 判断当前map集合中是否包含指定的Value
        System.out.println(map.get(9527));//女儿国国王 根据key值获取到对应的value值
        System.out.println(map.remove(9529)); //鲤鱼精 根据此key值删除对应指定的键值对,key值(键)与Value(值)都删了
        System.out.println(map.containsKey(9529)); //false
        System.out.println(map.containsValue("鲤鱼精")); //false

        //将map集合中的所有value取出,放入Collection集合中
        //Collection<Type>中Type的类型,取决于map中value的类型
        Collection<String> values = map.values();
        System.out.println(values);//[女儿国国王, 黑熊精, 黄毛怪, 黑熊精]

        //4.map集合的迭代方式一
        /**方式一:
         * 遍历map中的数据,但是map本身没有迭代器,所以需要先转换成set集合
         * Set<Key>:把map中的所有key值存入到set集合当中--keySet()*/
        //4.1将map集合中的key值取出存入set集合中,集合的泛型就是key的类型Integer
        Set<Integer> keySet = map.keySet();
        //4.2想要遍历集合就需要获取集合的迭代器
        Iterator<Integer> it = keySet.iterator();
        //4.3循环迭代集合中的所有元素
        while(it.hasNext()){//判断是否有下一个元素可以迭代
            Integer key = it.next();//拿到本轮循环中获取到的map的key
            String value = map.get(key);
            System.out.println("{"+key+","+value+"}");
        }

        /**方式二:
         * 遍历map集合,需要把map集合先转成set集合
         * 是把map中的一对键值对key&value作为一个Entry<K,V>整体放入set
         * 一对K,V就是一个Entry*/
        Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
        //获取迭代器
        Iterator<Map.Entry<Integer, String>> it2 = entrySet.iterator();
        while(it2.hasNext()){//判断是否有下一个元素可迭代
            //本轮遍历到的一个Entry对象
            Map.Entry<Integer, String> entry = it2.next();
            Integer key = entry.getKey();//获取Entry中的key
            String value = entry.getValue();//获取Entry中的value
            System.out.println("{"+key+","+value+"}");
        }
    }
}

map转set的流程图

在这里插入图片描述

2 HashMap

2.1 前言

  1. HashMap的键要同时重写hashCode()和equlas()
  2. hashCode()用来判定二者的hash值是否相同,重写后根据属性生成
  3. equlas()用来判断属性的值是否相同,重写后,根据属性判断
    - equlas()判断数据如果相等,hashCode()必须相同
    - equlas()判断数据如果不等,hashCode()尽量不同

2.2 概述

HashMap底层是一个Entry[ ]数组,长度为16,当存放数据时,会根据hash算法来计算数据的存放位置
算法:hash(key)%n , n就是数组的长度,其实也就是集合的容量
当计算的位置没有数据的时候,会直接存放数据
当计算的位置,有数据时,会发生hash冲突/hash碰撞,解决的办法就是采用链表的结构,在数组中指定位置处已有元素之后插入新的元素,也就是说数组中的元素都是最早加入的节点

  1. HashMap的结构是数组+链表 或者 数组+红黑树的形式
  2. HashMap底层的Entry[ ]数组,初始容量为16,加载因子是0.75f,扩容按约为2倍扩容
  3. 当存放数据时,会根据hash(key)%n算法来计算数据的存放位置,n就是数组的长度,其实也就是集合的容量
  4. 当计算到的位置之前没有存过数据的时候,会直接存放数据
  5. 当计算的位置,有数据时,会发生hash冲突/hash碰撞
    解决的办法就是采用链表的结构,在数组中指定位置处以后元素之后插入新的元素
    也就是说数组中的元素都是最早加入的节点
  6. 如果链表的长度>8时,链表会转为红黑树,当链表的长度<6时,会重新恢复成链表
    在这里插入图片描述

2.3 练习:获取HashMap的数据

package cn.tedu.collection;

import java.util.HashMap;

/**本类用于HashMap的练习*/
public class TestHashMap {
	public static void main(String[] args) {
		//创建HashMap对象
		HashMap<Integer,String> map = new HashMap();
		/**
		 * 源码摘抄:
		 * static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
		 * 初始容量为1<<4,相当于1*(2^4)=16
		 * static final float DEFAULT_LOAD_FACTOR = 0.75f;
		 * 默认的加载因子是0.75,也就是说存到75%开始扩容,按照2的次幂进行扩容
		 */
		/*
		 * 达到容量的加载因子后,就会重新开辟空间,重新计算所有对象的存储位置,也叫做rehash
		 * 设置初始容量与加载因子要讲求相对平衡,如果加载因子过低,则rehash过于频繁,影响性能
		 * 如果初始容量设置太高或者加载因子设置太高,影响查询效率
		 */
	}
}

2.4 练习:字符串中字符统计

package cn.tedu.collection;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
 * 本类用来完成Map集合相关练习
 * 需求:提示并接收用户输入的一串字符,并且统计出每个字符出现的次数
 * */
public class TestMap2 {
	public static void main(String[] args) {
		//1.提示用户输入要统计的字符串
		System.out.println("请输入您要统计的字符串");
		//2.接收用户输入的要统计的字符串
		String input = new Scanner(System.in).nextLine();
		//3.获取到用户输入的每个字符,String底层维护的是char[]
		//创建map集合存放数据,格式:{b=2,d=4,g=3}
		/**统计的是每个字符出现的次数,所以字符是char类型,次数是int,但是不可以使用基本类型,需要使用包装类型*/
		Map<Character,Integer> map = new HashMap();
		
		//开始位置:0 - 数组的第一个元素
		//结束位置:<input.length() 或者 <=input,length()-1
		//如何变化:++
		for (int i = 0; i < input.length(); i++) {
			char key = input.charAt(i);//获取一串字符中指定位置上的字符
			System.out.println("获取到的第"+(i+1)+"个字符:"+key);
			
			//4.统计每个字符出现的个数,存起来,存到map
			Integer value = map.get(key);//要先拿着key到map中找是不是有value
			if(value == null) {//如果判断为null,说明之前没有存过这个字符
				map.put(key, 1);//把当前的字符作为key存入,次数存1
			}else {
				map.put(key,value+1);//如果存过值,给之前的次数+1
			}
		}
		System.out.println("各个字符出现的频率为:");
		System.out.println(map);
	}
}

3 set接口

在这里插入图片描述

3.1 概述

  1. Set是一个不包含重复数据的Collection
  2. Set集合中的数据是无序的(因为Set集合没有下标)
  3. Set集合中的元素不可以重复 – 常用来给数据去重

3.2 Set集合的特点

  1. 数据无序且数据不允许重复
  2. HashSet : 底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复。
  3. TreeSet : 底层是TreeMap,也是红黑树的形式,便于查找数据

3.3 常用方法

学习Collection接口中的方法即可

3.4 HashSet

3.4.1 概述

底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K存入内部的HashMap中,其中K不允许重复,允许使用null.
在这里插入图片描述
在这里插入图片描述

3.5 练习: Set相关测试

package cn.collection;

import java.util.*;
/* 本类用于测试map接口*/
public class TestMap {
    public static void main(String[] args) {
        //1.创建MAP对象
        Map<Integer,String> map = new HashMap();
        map.put(9527,"白骨精");
        map.put(9528,"黑熊精");
        map.put(9529,"鲤鱼精");
        map.put(9530,"黄毛精");
        map.put(9531,"黑熊精");
        map.put(9527,"女儿国国王");
        System.out.println(map);
        /*
        1.map中存放的都是无序的数据,
        2.map中的value可以重复,比如我们放入了俩个黑熊精
        3.map中的key不能重复,如果重复,新的会将旧的覆盖掉
        */

        //3.进行方法测试
        //map.clear();//清空集合
        System.out.println(map.hashCode());//474360095 获取集合的哈希码
        System.out.println(map.equals("黄毛精"));//false  判断“黄毛怪”是否与集合对象相等
        System.out.println(map.isEmpty());//false  判断集合是否为空
        System.out.println(map.size());//5  获取集合中键值对的个数

        System.out.println(map.containsKey(9527));//true 判断当前map集合中是否包含指定的Key键
        System.out.println(map.containsValue("黑熊精"));//true 判断当前map集合中是否包含指定的Value
        System.out.println(map.get(9527));//女儿国国王 根据key值获取到对应的value值
        System.out.println(map.remove(9529)); //鲤鱼精 根据此key值删除对应指定的键值对,key值(键)与Value(值)都删了
        System.out.println(map.containsKey(9529)); //false
        System.out.println(map.containsValue("鲤鱼精")); //false

        //将map集合中的所有value取出,放入Collection集合中
        //Collection<Type>中Type的类型,取决于map中value的类型
        Collection<String> values = map.values();
        System.out.println(values);//[女儿国国王, 黑熊精, 黄毛怪, 黑熊精]

        //4.map集合的迭代方式一
        /**方式一:
         * 遍历map中的数据,但是map本身没有迭代器,所以需要先转换成set集合
         * Set<Key>:把map中的所有key值存入到set集合当中--keySet()*/
        //4.1将map集合中的key值取出存入set集合中,集合的泛型就是key的类型Integer
        Set<Integer> keySet = map.keySet();
        //4.2想要遍历集合就需要获取集合的迭代器
        Iterator<Integer> it = keySet.iterator();
        //4.3循环迭代集合中的所有元素
        while(it.hasNext()){//判断是否有下一个元素可以迭代
            Integer key = it.next();//拿到本轮循环中获取到的map的key
            String value = map.get(key);
            System.out.println("{"+key+","+value+"}");
        }

        /**方式二:
         * 遍历map集合,需要把map集合先转成set集合
         * 是把map中的一对键值对key和value作为一个Entry<K,V>整体放入set
         * 一对K,V就是一个Entry*/
        Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
        //获取迭代器
        Iterator<Map.Entry<Integer, String>> it2 = entrySet.iterator();
        while(it2.hasNext()){//判断是否有下一个元素可迭代
            //本轮遍历到的一个Entry对象
            Map.Entry<Integer, String> entry = it2.next();
            Integer key = entry.getKey();//获取Entry中的key
            String value = entry.getValue();//获取Entry中的value
            System.out.println("{"+key+","+value+"}");
        }
    }
}

4 拓展

HashMap扩容
成长因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
前面的讲述已经发现,当你空间只有仅仅为10的时候是很容易造成2个对象的hashcode 所对应的地址是一个位置的情况。这样就造成 2个 对象会形成散列桶(链表)。这时就有一个加载因子的参数,值默认为0.75 ,如果你hashmap的 空间有 100那么当你插入了75个元素的时候 hashmap就需要扩容了,不然的话会形成很长的散列桶结构,对于查询和插入都会增加时间,因为它要一个一个的equals比较。但又不能让加载因子很小,如0.01,这样显然是不合适的,频繁扩容会大大消耗你的内存。这时就存在着一个平衡,jdk中默认是0.75,当然负载因子可以根据自己的实际情况进行调整。

4.1 作业:完成笔记HashMap的相关练习:2.4 练习:字符串中字符统计

需求:随机输入一串字符串,统计这串字符串中每个字母的出现次数

package cn.map;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

//练习:字符串中字符统计
//需求:随机输入一串字符串,统计这串字符串中每个字母的出现次数
public class TeatMap {
    public static void main(String[] args) {
        //1.接收用户输入的字符串
        System.out.println("请输入您要测试的字符串:");
        String input = new Scanner(System.in).nextLine();

        //2.准备一个map集合,用来存放出现的字符与字符的个数
        //因为map不能使用基本类型,所以需要使用char的包装类character与字符的个数integer
        //要确定key值与value值,key不能 重复,value可以重复
        Map<Character, Integer> map = new HashMap<>();

        //3.准备要存入map中的数据,K和V
        //3.1遍历用户输入的字符串,统计每个字符
        for (int i = 0; i < input.length(); i++) {
            //3.2获取本轮循环中遍历到的字符
            char key = input.charAt(i);
            //System.out.println(key);  //打印查看每轮循环获取到的字符,有没有问题
            //因为integer初始值为null,不能加一,所以要分情况讨论
            //3.3根据获取到的key拿到对应的value
            Integer value = map.get(key); //根据字符,获取map中这个字符保存的次数
            if(value==null){//之前这个字符没有出现过,次数还是Integer的默认值
                map.put(key,1); //没有出现过,次数就设置为1
            }else{
                map.put(key,value+1);//之前出现过,次数变为之前的次数+1
            }
        }
        System.out.println("各个字符出现的次数为:"+map);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值