Java集合学习之HashMap(映射基本概念,HashMap基本用法,put源码分析)

一.映射

1.为什么要用映射?

在真正学习HashMap之前我们先了解下map的基本概念,在java中map通常表示一种映射关系。
集是一个集合,它可以快速地查找现有地元素,但是,要查看一个元素,需要有要查找元素的精确副本,这不是一种非常通用的查找方式。通用的方式是我们知道某些键的信息,并想要查找与之对应的元素。 映射(map)数据结构就是为此设计的。

2.什么是映射?

映射(map)用来存放键/值对,如果我们提供了键,就可以找到相应的值。

二.HashMap基础学习

1.何为HashMap?

HashMap是对键进行Hash来存储数据的一种数据结构的实现,它主要实现了Map接口和继承了骨架实现,使用它我们可以对数据进行快速存储和查找。

2.构造方法

构造方法描述
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 。

3.基本操作方法

方法描述
void clear()从这张地图中删除所有的映射。
Object clone()返回此 HashMap实例的浅拷贝:键和值本身不被克隆。
boolean containsKey(Object key)如果此映射包含指定键的映射,则返回 true 。
V get(Object key)返回到指定键所映射的值,或 null如果此映射包含该键的映射。
boolean isEmpty()如果此地图不包含键值映射,则返回 true 。
Set keySet()返回此地图中包含的键的Set视图。
V put(K key, V value)将指定的值与此映射中的指定键相关联。
V remove(Object key)从该地图中删除指定键的映射(如果存在)。
V replace(K key, V value)只有当目标映射到某个值时,才能替换指定键的条目。
int size()返回此地图中键值映射的数量。

4.使用例子

Employee.java

package JavaHeXinJiShu;
public class Employee
{
   private String name;
   private double salary;
   public Employee(String name)
   {
      this.name = name;
      salary = 0;
   }

   public String toString()
   {
      return "[name=" + name + ", salary=" + salary + "]";
   }
}

example9_6.java

package JavaHeXinJiShu;

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

public class example9_6 {
    public static void main(String[] args)
    {
        Map<String, Employee> staff = new HashMap<>();
        staff.put("144-25-5464", new Employee("Amy Lee"));
        staff.put("567-24-2546", new Employee("Harry Hacker"));
        staff.put("157-62-7935", new Employee("Gary Cooper"));
        staff.put("456-62-5527", new Employee("Francesca Cruz"));

        // 打印hashmap中的所有键值对
        System.out.println(staff);

        //删除键名为567-24-2546的键值对
        staff.remove("567-24-2546");

        // 覆盖456-62-5527原有键值对
        staff.put("456-62-5527", new Employee("Francesca Miller"));

        // 查看键为157-62-7935的值
        System.out.println(staff.get("157-62-7935"));

        //遍历所有键值对
        staff.forEach((k, v) ->
                System.out.println("key=" + k + ", value=" + v));
    }
}

在这里插入图片描述

二.下面我们通过分析上面的例子来深入学习

1.构造方法分析

 Map<String, Employee> staff = new HashMap<>();

通过上面的语句我们构建了一个HashMap的实例对象,我们知道默认对象的负载因子为0.75,容量为16.

2.走进put方法(源码分析)

 staff.put("144-25-5464", new Employee("Amy Lee"));

我们下面来看看,上面语句到底做了些什么?
put的源代码为:

 public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

我们可以知道,它的返回类型和值的类型一致,参数为一对键值。
方法体里只是放回了调用putVal方法的返回值。

上面pubVal中传入了五个参数:
hash(key),key,value,false,true
其中hash方法为:

 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

它的作用是计算并返回key的哈希值。
下面我们把例子带入:
首先,我们向put中传进key=“144-25-5464” 一个字符串。value=new Employee(“Amy Lee”) 一个对象。
然后,将key哈希过的值,key,value,false,true传入putVal方法中。

下面我们来进入putVal方法:

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
                   
        Node<K,V>[] tab; //定义节点数组tab
        Node<K,V> p; //定义节点p
        int n, i;
        
        //初始化tab
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
            
        //如果要加入的键值对中的键不在其中,添加之   
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        //否则覆盖之
        else {
            Node<K,V> e; 
            K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
             //如果是覆盖存储的返回覆盖后的value值
            if (e != null) {
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;//新存储的返回null
    }

也就是说:

 staff.put("144-25-5464", new Employee("Amy Lee"));

上面的语句完成了将key=“144-25-5464”,value= new Employee(“Amy Lee”)的键值对存入我们的HashMap对象中且如果HashMap中原本存在此键对应的value值返回此value对象,否则返回null。

3.看看get方法

 public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

原理很简单,就不解析了。

上面主要解析了put方法,还有其他方法的探索我们可以在这个基础上来进行。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值