Java学习分享 ---【集合】Map

目录

一、Interface Map

1.概念介绍

1.1基本概念 

1.2 官方说明文档

1.3 异常类型

2.Collection和Map的区别和间接联系

3.Map接口中提供的常用方法

4.HashMap

4.1 HashMap类介绍

4.2 HashMap类特点

4.3 HashMap构造方法

4.5 HashMap集合存储对象

4.6 具体详解参考

4.7 HashMap底层源码解析

5.TreeMap

5.1 TreeMap类介绍

5.2 TreeMap类特点

5.3 TreeMap构造方法

5.4 TreeMap常用方法、


一、Interface Map

1.概念介绍

Interface Map<K,V> 双边队列 键值对

1.1基本概念 

Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含一个键(key)对象和一个值(value)对象。用于保存具有映射关系的数据。

Map 集合里保存着两组值,一组值用于保存 Map 里的 key,另外一组值用于保存 Map 里的 value,key 和 value 都可以是任何引用类型的数据。Map 的 key 不允许重复,value 可以重复,即同一个 Map 对象的任何两个 key 通过 equals 方法比较总是返回 false。

Map 中的 key 和 value 之间存在单向一对一关系,即通过指定的 key,总能找到唯一的、确定的 value。从 Map 中取出数据时,只要给出指定的 key,就可以取出对应的 value。

Map 接口主要有两个实现类:HashMap 类和 TreeMap 类。其中,HashMap 类按哈希算法来存取键对象,而 TreeMap 类可以对键对象进行排序。

1.2 官方说明文档

public interface Map<K,V>

将键映射到值的对象。 Map不能包含重复的键; 每个键可以映射到最多一个值。

这个接口取代了Dictionary类

Map界面提供了三个集合视图 ,允许将映射内容视为一组键,值集合或键值映射集合。 

注意:如果使用可变对象作为Map中的键,必须非常小心。 如果对象的值以影响equals比较的方式更改,而对象是Map中的键,则不会指定Map的行为。 这个禁令的一个特殊情况是,Map不允许将自己包含在内。 虽然Map可以将其本身作为一个值,但建议您非常小心: equals和hashCode方法在这样的Map上已经不太明确。

所有通用映射实现类应提供两个“标准”构造函数:一个创建空映射的void(无参数)构造函数,以及一个具有类型为Map的单个参数的构造函数 ,它创建一个具有相同键值的新映射映射作为参数。 实际上,后一个构造函数允许用户复制任何地图,产生所需类的等效地图。 没有办法强制执行此建议(因为接口不能包含构造函数),而JDK中的所有通用映射实现都符合要求。

一些地图实现对它们可能包含的键和值有限制。 例如,一些实现禁止空键和值,有些对键的类型有限制。

        尝试插入不合格的键或值会抛出未经检查的异常,通常为NullPointerException或ClassCastException 。

         尝试查询不合格键或值的存在可能会引发异常,或者可能只是返回false; 一些实现将展现出前者的行为,一些实现将展现出后者。

         更一般来说,尝试对不符合条件的密钥或值的操作,其完成不会导致将不合格元素插入到地图中可能会导致异常或可能成功执行该选项。

执行递归遍历Map的一些地图操作可能会失败,并且Map直接或间接包含自身的自引用实例有异常。 这包括clone()equals()hashCode()toString()方法。

1.3 异常类型

给定一个键和一个值,你可以将该值存储在一个 Map 对象。之后,你可以通过键来访问对应的值。

当访问的值不存在的时候,方法就会抛出一个 NoSuchElementException 异常。

当对象的类型和 Map 里元素类型不兼容的时候,就会抛出一个 ClassCastException 异常。

当在不允许使用 Null 对象的 Map 中使用 Null 对象,会抛出一个 NullPointerException 异常。

当尝试修改一个只读的 Map 时,会抛出一个 UnsupportedOperationException 异常。

2.Collection和Map的区别和间接联系

1. 区别:
Collection : 单列集合。
Map : 双列集合,键和值一一映射,针对键有效,跟值无关。
2. 间接联系:
HashSet 集合的add()方法依赖于Map接口的子实现类HashMap集合的put()方法
TreeSet 集合的add()方法依赖于Map接口的子实现类TreeMap 集合的put()方法

3.Map接口中提供的常用方法

增:
V put(K key,V value); 
//添加键值对到map集合中,返回值是value

void putAll(Map<? extends K,? extends V> m) 
//将一个map集合添加到另一个map集合中

删:

void clear()

//删除所有映射
V remove(Object key); 
//通过键删除键值对的数据,返回值是被删除的值

改:
V put(K key,V value); 
//当键值对存在时,将value值覆盖,前提时键要一致

查:
int size();
//查看map集合中元素的个数

boolean isEmpty()  
//判断map集合是否为空,为空则返回true

boolean containsKey(Object key);
//判断map集合是否包含这个建

boolean containsValue(Object value);
//判断map集合中是否有这个值

重要的方法;
V get(Object key); 
//通过键获取值,返回值是value

Set<K> keySet(); //set集合元素不重复
//获取map集合中所有的键,然后存到Set集合中,返回值是set<k>集合,所以要Set<k>一个set集合去接

Collection<V> values();
//Collection集合元素可重复,且可以用ArrayList或LinkedList实现
//获取map集合中的值,存到Collection集合中  

Set<Map.Entry<K,V>> entrySet(); 


//将map集合的键值对,存到了Set集合中  
        Map.Entry接口的方法:
        getKey():返回键值对的键
        getValue():返回键值对的值
        setValue():用指定的值替换

4.HashMap

4.1 HashMap类介绍

4.1.1 基本介绍

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。

HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。

HashMap 是无序的,即不会记录插入的顺序。(但是其中数据会根据hash值默认排序)

HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。

主要特征包括:Key可存NULL、无序、线程不安全、 底层使用了数组+链表+红黑树的结构。

4.1.2 官方相关

public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
  • 基于哈希表的实现的Map接口。 此实现提供了所有可选的地图操作,并允许null的值和null键。

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

  • HashMap的一个实例有两个影响其性能的参数: 初始容量负载因子容量是哈希表中的桶数,初始容量只是创建哈希表时的容量。 负载因子是在容量自动增加之前允许哈希表得到满足的度量。 当在散列表中的条目的数量超过了负载因数和电流容量的乘积,哈希表被重新散列 (即,内部数据结构被重建),使得哈希表具有桶的大约两倍。

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

  • 如果许多映射要存储在HashMap实例中,则以足够大的容量创建映射将允许映射的存储效率高于使其根据需要执行自动重新排序以增长表。 请注意,使用同一个hashCode()多个密钥是降低任何哈希表的hashCode()的一种方法。 为了改善影响,当按键是Comparable时,这个类可以使用键之间的比较顺序来帮助打破关系。

  • 请注意,此实现不同步。 如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改了映射,那么它必须在外部进行同步。 (结构修改是添加或删除一个或多个映射的任何操作;仅改变与实例已经包含的密钥相关联的值不是结构修改。)这通常通过对自然地封装映射的一些对象进行同步来实现。 如果没有这样的对象存在,Map应该使用Collections.synchronizedMap方法“包装”。 这最好在创建时完成,以防止意外的不同步访问地图:

      Map m = Collections.synchronizedMap(new HashMap(...)); 
  • 所有这个类的“集合视图方法”返回的迭代器都是故障快速的 :如果映射在迭代器创建之后的任何时间被结构地修改,除了通过迭代器自己的remove方法之外,迭代器将抛出一个ConcurrentModificationException 。 因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来未确定的时间冒着任意的非确定性行为。

  • 请注意,迭代器的故障快速行为无法保证,因为一般来说,在不同步并发修改的情况下,无法做出任何硬性保证。 失败快速迭代器尽力扔掉ConcurrentModificationException 。 因此,编写依赖于此异常的程序的正确性将是错误的:迭代器的故障快速行为应仅用于检测错误。

4.2 HashMap类特点

14问(仅供学习参考,侵删)

JAVA - HashMap特点及小结_wabinog的博客-CSDN博客_hashmap特点

HashMap夺命14问,你能坚持到第几问? - 知乎

4.3 HashMap构造方法

HashMap() 构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。
HashMap(int initialCapacity) 构造一个空的 HashMap具有指定的初始容量和默认负载因子(0.75)。
HashMap(int initialCapacity, float loadFactor) 构造一个空的 HashMap具有指定的初始容量和负载因子。
HashMap(Map<? extends K,? extends V> m) 构造一个新的 HashMap与指定的相同的映射 Map

4.4 遍历HashMap集合

4.4.1 在 for 循环中使用 entries 实现 Map 的遍历(最常见和最常用的)

public static void main(String[] args) {
    Map<String, String> map = new HashMap<String, String>();
    map.put("北京", "朝阳");
    map.put("浙江", "杭州");
    for (Map.Entry<String, String> entry : map.entrySet()) {
        String mapKey = entry.getKey();
        String mapValue = entry.getValue();
        System.out.println(mapKey + ":" + mapValue);
    }
}

4.4.2 使用 for-each 循环遍历 key 或者 values,一般适用于只需要 Map 中的 key 或者 value 时使用。性能上比 entrySet 较好。

public static void main(String[] args) {
    Map<String, String> map = new HashMap<String, String>();
    map.put("北京", "朝阳");
    map.put("浙江", "杭州");
    // 打印键集合
    for (String key : map.keySet()) {
        System.out.println(key);
    }
    // 打印值集合
    for (String value : map.values()) {
        System.out.println(value);
    }
}

4.4.3 使用迭代器(Iterator)遍历

public static void main(String[] args) {
    Map<String, String> map = new HashMap<String, String>();
    map.put("北京", "朝阳");
    map.put("浙江", "杭州");
    Iterator<Entry<String, String>> entries = map.entrySet().iterator();
    while (entries.hasNext()) {
        Entry<String, String> entry = entries.next();
        String key = entry.getKey();
        String value = entry.getValue();
        System.out.println(key + ":" + value);
    }
}

4.4.4 通过键找值遍历,这种方式的效率比较低,因为本身从键取值是耗时的操作。

for(String key : map.keySet()){
    String value = map.get(key);
    System.out.println(key+":"+value);
}

4.5 HashMap集合存储对象

示例代码:

import java.util.*;

class Person{
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Demo02 {
    public static void main(String[] args) {
        List<Person> listPeople1 = new ArrayList<>();
        listPeople1.add(new Person("张胜男",24));
        listPeople1.add(new Person("张胜",24));
        listPeople1.add(new Person("张男",24));

        List<Person> listPeople2 = new ArrayList<>();
        listPeople2.add(new Person("六胜男",24));
        listPeople2.add(new Person("刘胜男",24));
        listPeople2.add(new Person("柳胜男",24));

        Map<String, List<Person>> map1 = new HashMap<>();
        map1.put("001",listPeople1);
        map1.put("002",listPeople2);
        System.out.println(map1);
        System.out.println("----------------------");
        //取值
        Collection<List<Person>> values1 = map1.values();
        for (List<Person> people : values1) {
            System.out.println(people.get(0));
            for (Person person : people) {
                System.out.println("姓名" + person.name);
            }
        }
        System.out.println("----------------------");


        //键值(value)为对象
        Map<Integer,Person> map = new HashMap<>();
        //存值
        map.put(1,new Person("王五",12));
        map.put(2,new Person("张三",13));
        map.put(3,new Person("李四",16));
        map.put(4,new Person("赵谦",22));
        map.put(5,new Person("朱八",34));
        System.out.println(map);
        //取值
            //取键
        Set<Integer> set = map.keySet();
        System.out.println(set);
            //取值
        Collection<Person> values = map.values();
        System.out.println(values);
            //取值 中的 对象 的 属性

        //使用增强for循环
        for (Person value : values) {
            System.out.println("姓名:" + value.name);
        }

        //使用entrySet
        Set<Map.Entry<Integer, Person>> entries = map.entrySet();
        for (Map.Entry<Integer, Person> entry : entries) {
            System.out.println("年龄" + entry.getValue().age);
        }
    }
}
//输出结果:
{001=[Person{name='张胜男', age=24}, Person{name='张胜', age=24}, Person{name='张男', age=24}], 002=[Person{name='六胜男', age=24}, Person{name='刘胜男', age=24}, Person{name='柳胜男', age=24}]}
----------------------
Person{name='张胜男', age=24}
姓名张胜男
姓名张胜
姓名张男
Person{name='六胜男', age=24}
姓名六胜男
姓名刘胜男
姓名柳胜男
----------------------
{1=Person{name='王五', age=12}, 2=Person{name='张三', age=13}, 3=Person{name='李四', age=16}, 4=Person{name='赵谦', age=22}, 5=Person{name='朱八', age=34}}
[1, 2, 3, 4, 5]
[Person{name='王五', age=12}, Person{name='张三', age=13}, Person{name='李四', age=16}, Person{name='赵谦', age=22}, Person{name='朱八', age=34}]
姓名:王五
姓名:张三
姓名:李四
姓名:赵谦
姓名:朱八
年龄12
年龄13
年龄16
年龄22
年龄34

4.6 具体详解参考

(侵删)

Java集合之一—HashMap_woshimaxiao1的博客-CSDN博客_hashmap

HashMap 详解_与忘_的博客-CSDN博客_hashmap

Java中HashMap详解_攻城狮Chova的博客-CSDN博客_java中的hashmap

4.7 HashMap底层源码解析

(侵删)

HashMap底层源码解析下(超详细图解)_温文艾尔的博客-CSDN博客_hashmap底层源码

HashMap 的底层源码分析_妙先森的博客-CSDN博客_hashmap底层源码

5.TreeMap

5.1 TreeMap类介绍

基本介绍:

  • 会对集合内的元素排序,可以在 new 的时候添加比较器
  • 默认按照 key 进行升序排序,也可以自定义排序规则
  • 底层仍是红黑树

官方:

  • public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, Serializable
  • 一个红黑树基于NavigableMap实现。 该地图是根据排序natural ordering其密钥,或通过Comparator在地图创建时提供,这取决于所使用的构造方法。

  • 此实现提供了保证的log(n)时间成本containsKeygetputremove操作。 算法是Cormen,Leiserson和Rivest的算法介绍中的算法的适应性

  • 如果这个排序的映射要正确地实现Map接口,那么由树映射维护的排序(如任何排序的映射)以及是否提供显式比较器都必须equals Map一致。 (参见ComparableComparator一致的精确定义与equals)。这是因为该Map接口在来定义equals的操作,但一个排序映射使用它所有的键比较compareTo (或compare )方法,于是两个从排序图的角度来说,通过该方法认为相等的键是相等的。 排序地图的行为明确定义的,即使其排序与equals ; 它只是没有遵守Map界面的总体合同。

5.2 TreeMap类特点

  • TreeMap 基于红黑树实现,增删改查的平均和最差时间复杂度均为 O(logn) ,最大特点是 Key 有序。
    Key 必须实现 Comparable 接口或提供的 Comparator 比较器,所以 Key 不允许为 null。

  • HashMap 依靠 hashCode 和 equals 去重,而 TreeMap 依靠 Comparable 或 Comparator。
    TreeMap 排序时,如果比较器不为空就会优先使用比较器的 compare 方法,否则使用 Key 实现的 Comparable 的 compareTo 方法,两者都不满足会抛出异常。

  • TreeMap 通过 put 和 deleteEntry 实现增加和删除树节点。

  • 插入新节点的规则有三个:

  • 需要调整的新节点总是红色的。
  • 如果插入新节点的父节点是黑色的,不需要调整。
  • 如果插入新节点的父节点是红色的,由于红黑树不能出现相邻红色,进入循环判断,通过重新着色或左右旋转来调整。
    TreeMap 的插入操作就是按照 Key 的对比往下遍历,大于节点值向右查找,小于向左查找,先按照二叉查找树的特性操作,后续会重新着色和旋转,保持红黑树的特性。

5.3 TreeMap构造方法

TreeMap() 使用其键的自然排序构造一个新的空树状图。
TreeMap(Comparator<? super K> comparator) 构造一个新的,空的树图,按照给定的比较器排序。
TreeMap(Map<? extends K,? extends V> m) 构造一个新的树状图,其中包含与给定地图相同的映射,根据其键的 自然顺序进行排序
TreeMap(SortedMap<K,? extends V> m) 构造一个包含相同映射并使用与指定排序映射相同顺序的新树映射。

5.4 TreeMap常用方法、

size()
containsKey(K)
put(K, V)
get(K)
remove(Object)
containsValue(V)
firstKey():获取按照排序规则的第一个键值对的键
firstEntry():获取按照排序规则的第一个键值对
lastKey():获取按照排序规则的最后一个键值对的键
lastEntry():获取按照排序规则的最后一个键值对
floorKey(K):获取当前键或者比当前键小的键中最大的键
floorEntry(K):获取当前键或者比当前键小的键中最大的键对应的键值对
ceilingKey(K):获取当前键或者比当前键大的键中最小的键
ceilingEntry(K):获取当前键或者比当前键大的键中最小的键对应的键值对

方法使用示例:(侵删)

java中TreeMap常用方法_java中TreeMap集合的常用方法_引长弓的博客-CSDN博客

Java之TreeMap常用methods_山淼的博客-CSDN博客_java treemap 方法

5.5 遍历TreeMap集合

treeMap 的多种遍历方式以及比较器和comparable的实现_天羽@的博客-CSDN博客_treemap遍历

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值