map集合---------今日份下饭菜,妈妈再也不用担心我饿肚子了,学好List, Set,与Map,装的下,世界就是你的

本文详细介绍了Map接口及其常用方法,包括HashMap和Hashtable的区别。HashMap提供常量时间复杂度的基本操作,适用于非同步场景,而Hashtable是同步的,适合多线程。HashMap在JDK1.8中引入了红黑树优化,提高了性能。文章还讨论了如何选择合适的Map实现以及它们的性能特征。
摘要由CSDN通过智能技术生成
“靡不有初,鲜克有终”,读音是“mǐ bù yǒu chū,xiǎn kè yǒu zhōng”,原意是凡事都有个开始,但经常不了了之,没个结果。后借此语以讽谕持志不终的人 ,用以告诫人们为人做事要善始善终。

今天也是要好好学习的一天,你学习了吗

Map 接口

在 Collection 集合中,每次操作的都是一个对象,如果现在假设要操作一对对象,比如,甲喜欢上了丙,但没有联系方法,但乙认识丙,乙和丙不是同一对象,但是通过乙可以找到丙,这就是映射关系,这种就必须使用 Map 了,类似于以下一种情况:

姓名身份证号
张三360278123456…
李四360678234567…

由于业务需求,名字和身份证号要分开存放,身份证号的唯一性和键(key)相得益彰,将身份证号存放在key中,必然可以通过唯一的键找到对应的值,不同的键可以有相同的值(可以有很多个张三(value),但身份证号(key)唯一),Map 接口里面的所有内容都按照 key -> value 的形式保存,键值对,也称为二元偶对象。

此接口定义如下:

Interface Map<K,V>

  • 参数类型

    K – 此映射维护的密钥类型

    V – 映射值的类型 (K,V泛型类型,是操作者指定的类型)

    Interface Map.Entry<K,​V> (Map的内部接口)键值对就是entry。 Map.entrySet方法返回 Map的集合视图,其元素属于此类

此接口与 Collection 接口没有任何的关系(并列的顶级集合接口,一个单值,一个键值对),是第二大的集合操作接口。

此接口常用方法如下:

No.方法名称类型描述
1void clear()普通清空 Map 集合中的内容
2boolean containsKey(Object key)普通判断集合中是否存在指定的 key
3boolean containsValue(Object value)普通判断集合中是否存在指定的 value
4Set<Map.Entry<K,V>> entrySet()普通 Map 接口变为 Set 集合
5V get(Object key)普通根据 key 到其对应的 value
6boolean isEmpty()普通判断是否为空
7Set keySet()普通将全部的 key 变为 Set 集合
8Collection values()普通将全部的 value 变为 Collection 集合
9V put(K key,V value)普通向集合中增加内容
10void putAll(Map<? extends K,? extends V> m)普通增加一组集合
11V remove(Object key)普通根据 key 删除内容

Map接口提供了三个collection-view(collection集合视图) ,这些视图允许将映射的内容视为一组键集合(类型:Set ,方法: keySet()),值集合(类型:Collection , 方法:values())或键值映射集(类型:Set<Map.Entry<K,V>> , 方法 entrySet())。

Map的顺序被定义为其中在Map上的collection-view迭代返回元素的顺序。 一些Map实现,如TreeMap类,对其顺序做出了具体保证; 其他的,比如HashMap集合,没有。

注意:如果将可变对象用作映射键,则必须非常小心(最好不要将可变对象用作键值,后果很严重)。 如果在对象是Map中的键时,以影响equals比较的方式更改对象的值,则不指定映射的行为。 这种禁令的一个特例是,Map不允许将自己作为一个关键词。 虽然允许地图将自身包含为值,但建议极为谨慎: equals和hashCode方法在此类Map上不再明确定义。

Map 本身是一个接口,所以一般会使用以下的几个子类:HashMap、TreeMap、Hashtable。

HashMap集合

HashMap 是 Map 的子类,此类的定义如下:

public class HashMap<K,V> extends AbstractMap<K,V>

implements Map<K,V>, Cloneable, Serializable

(此类继承了 AbstractMap 类,同时可以被克隆,可以被序列化下来。)

基于哈希表的Map接口的实现。此实现提供了所有可选的映射操作,并允许null值和null键。( HashMap类大致相当于Hashtable ,除了它是不同步的并且允许空值。)此类不保证元素的顺序;特别是它不保证元素会随着时间的推移保持不变(当其容量不足时会重建内部数据结构,即集合会自动重新散列来扩展表)。

该实现为基本操作( get和put )提供了恒定时间性能,假设散列函数在桶之间正确地分散元素。 对集合视图的迭代需要与HashMap实例的“容量”(桶数)加上其大小(键值映射的数量)成比例的时间。 因此,如果迭代性能很重要,则不要将初始容量设置得太高(或负载因子太低)非常重要。

HashMap的实例有两个影响其性能的参数: 初始容量和负载因子。 容量是哈希表中的桶数,初始容量只是创建哈希表时的容量。 加载因子是在自动增加容量之前允许哈希表获取的完整程度的度量。 当哈希表中的桶使用数超过加载因子和当前容量的乘积时,哈希表将被重新哈希(即重建内部数据结构),以便哈希表具有大约两倍的桶数。

作为一般规则,默认加载因子(0.75)在时间和空间成本之间提供了良好的折衷。 较高的值会减少空间开销,但会增加查找成本(反映在HashMap类的大多数操作中,包括get和put )。 在设置其初始容量时,应考虑映射中的预期条目数及其负载因子,以便最小化重新散列操作的数量。 如果初始容量大于最大条目数除以加载因子,则不会发生重新加载操作。

如果要将多个映射存储在HashMap实例中,则使用足够大的容量创建映射将允许映射更有效地存储,而不是根据需要执行自动重新散列来扩展表。 请注意,使用具有许多相同hashCode()的键是减慢任何哈希表性能的可靠方法(即想要提高性能就不要有多个相同hashCode()的键)。 为了改善影响,当键为Comparable时,此类可以使用键之间的比较顺序来帮助打破关系。

构造器

构造器描述
HashMap()使用默认初始容量(16)和默认加载因子(0.75)构造一个空 HashMap 。
HashMap(int initialCapacity)使用指定的初始容量和默认加载因子(0.75)构造一个空 HashMap 。
HashMap(int initialCapacity, float loadFactor)使用指定的初始容量和加载因子构造一个空 HashMap 。
HashMap(Map<? extends K,? extends V> m)构造一个新的 HashMap ,其映射与指定的 Map相同。

方法摘要 (标红注释是我的理解)

变量和类型方法描述
voidclear() 从此映射中删除所有映射。(清空)
Objectclone()返回此 HashMap实例的浅表副本:未克隆键和值本身。
Vcompute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)尝试计算指定键及其当前映射值的映射(如果没有当前映射, null )。
VcomputeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)如果指定的键尚未与值关联(或映射到 null ),则尝试使用给定的映射函数计算其值并将其输入此映射,除非 null 。
VcomputeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)如果指定键的值存在且为非null,则尝试在给定键及其当前映射值的情况下计算新映射。
boolean containsKey(Object key)如果此映射包含指定键的映射,则返回 true 。 (是否包含某键)
booleancontainsValue(Object value)如果此映射将一个或多个键映射到指定值,则返回 true 。 (是否包含某值)
Set<Map.Entry<K,V>> entrySet()返回此映射中包含的映射的Set视图。 (返回一个set集合)
Vget(Object key)返回指定键映射到的值,如果此映射不包含键的映射,则返回 null 。(根据键取值)
booleanisEmpty()如果此映射不包含键 - 值映射,则返回 true 。 (是否为空)
Set< K > keySet()返回此映射中包含的键的Set视图。 (调用此方法得到一个关于key的Set集合)
Vmerge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)如果指定的键尚未与值关联或与null关联,则将其与给定的非空值关联。
V put(K key, V value)将指定的值与此映射中的指定键相关联。(存入单个键值对)
voidputAll(Map<? extends K,? extends V> m)将指定映射中的所有映射复制到此映射。(存入整个m集合,相当于存入多个键值对)
Vremove(Object key)从此映射中删除指定键的映射(如果存在)。 (删除键值对)
intsize()返回此映射中键 - 值映射的数量。 (集合实际长度,不是容量)
Collection< V >values() (比较少用)返回此映射中包含的值的Collection视图。 (返回一个Collection集合)

HashMap集合底层存储数据的结构是哈希表

什么是哈希表呢?

在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。当其长度减小到6时又变回链表。(所以长度为7时是个坑,可能是链表也可能是红黑树。)

简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210706155951458.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81ODQyODIxNg==,size_16,color_FFFFFF,t_70

总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能。

Hashtable 集合

定义:public class Hashtable<K,​V>
extends Dictionary<K,​V>
implements Map<K,​V>, Cloneable, Serializable

该类实现了一个哈希表,它将键映射到值。任何非null对象都可以用作键或值。

要成功存储和检索哈希表中的对象,用作键的对象必须实现hashCode方法和equals方法。

此示例创建数字哈希表。 它使用数字的名称作为键:

   Hashtable<String, Integer> numbers = new Hashtable<String, Integer>(); 
   numbers.put("one", 1); 
   numbers.put("two", 2); 
   numbers.put("three", 3); 

要检索数字,请使用以下代码:

   Integer n = numbers.get("two"); 
   if (n != null) { 
   		System.out.println("two = " + n);
   } 

从Java 2平台1.2版本开始,该类被改进以实现Map接口,使其成为Java Collections Framework的成员。 与新的集合实现不同, Hashtable是同步的。 如果不需要线程安全实现,建议使用HashMap代替Hashtable 。 如果需要线程安全的高度并发实现,则建议使用ConcurrentHashMap代替Hashtable 。

构造器

构造器描述
Hashtable()使用默认初始容量(11)和加载因子(0.75)构造一个新的空哈希表。
Hashtable(int initialCapacity)使用指定的初始容量和默认加载因子(0.75)构造一个新的空哈希表。
Hashtable(int initialCapacity, float loadFactor)使用指定的初始容量和指定的加载因子构造一个新的空哈希表。
Hashtable(Map<? extends K,? extends V> t)构造一个新的哈希表,其具有与给定Map相同的映射。

方法

两者操作方法基本一样(map各子类集合的操作方法都差不多,同理其他集合也是)

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class Demo {
    //HashMap/Hashtable/ConcurrentHashMap
    //TreeMap
    //LinkedHashMap
    public static void main(String[] args) {
        //key 和 value 可以是自定义类型,key值建议不要随意更改
        Map<String, String> data = new TreeMap<>();
        data.put("key1", "赵钱孙李");//增加方法
        data.put("key2", "周吴郑王");
        data.put("key3", "1周吴郑王");
        data.put("key4", "小明同学");
        //boolean remove = data.remove("key4", "小明同");//false
        data.remove("key4");//删除方法
        int size = data.size();//集合内键值对数量
        System.out.println("集合内键值对数量:" + size);
        boolean empty = data.isEmpty();//集合是否为空
        System.out.println("集合是否为空:" + empty);
        data.put("", "");
        boolean key1 = data.containsKey("key1");//是否包含某个键,containsValue是否包含某个值
        System.out.println("集合是否包含key1:" + key1);
        //data.clear();清空集合
        //data.put(null, null);//只有HashMap和LinkedHashMap可以存储,其余会报空指针异常
        //data.put(null, null);//key不可重复,相同新value覆盖旧value
        System.out.println(data);
        System.out.println("-----------以上直接输出整个集合------------");
        /*String value = data.get("key1");
        System.out.println(value);
        value = data.get("key2");//根据键取值
        System.out.println(value);*/
        Set<String> set = data.keySet();
        for (String key :
                set) {
            System.out.println(key + "->" + data.get(key));
        }
        System.out.println("------以上将key转为Set集合遍历键调用get(key)方法取值--------");
        Collection<String> values = data.values();//把所有值转成Collection集合
        for (String value :
                values) {
            System.out.println(value);
        }
        System.out.println("---------将value转为Collection集合遍历值(无法得到key)--------------");
        for (Map.Entry<String, String> entry : data.entrySet()) {
            System.out.println(entry.getKey() + "->" + entry.getValue());
        }
        System.out.println("---------将entry转为Set集合遍历键值对--------------");
        Iterator<Map.Entry<String, String>> iterator = data.entrySet().iterator();
        //Iterator<String> iterator = data.keySet().iterator();
        //Iterator<String> iterator = data.values().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> next = iterator.next();
            System.out.println(next);
        }
        System.out.println("-------------还能用iterator迭代器输出---------------");
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ya5M7abx-1626851682934)(https://img- blog.csdnimg.cn/img_convert/2905d4fe3a5b92b72703386d9edbd9ee.png)]
在这里插入图片描述

HashMapHashtable 的区别

在整个集合中除了 ArrayList 和 Vector 的区别之外,另外一个最重要的区别就是 HashMap 与 Hashtable 的区别。

No.区别点HashMapHastable
1推出时间JDK 1.2 之后推出的,新的操作类JDK 1.0 时推出的,旧的操作类
2性能异步处理,性能较高同步处理,性能较低
3null允许设置为 null不允许设置,否则将出现空指向异常
4迭代快速失败快速失败
5初始容量(桶数)1611
6负载因子0.750.75

循环方式上面代码里有示例。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值