Java笔记:Map从入门到性能分析

简介: Java笔记:Map从入门到性能分析

Map从入门到性能分析


课程目标

HashMap的构造方法,合适的遍历,复制转换

HashMap的底层原理(存取、初始化、扩容)

TreeMap、LinkedHashMap的用法

性能分析

运行环境:

Idea

Java Version 1.8

Map接口及其实现类

1、继承关系


Map
    -HashMap 
        -LinkedHashMap
    -SortedMap
        -TreeMap

2、Map接口通用的方法

// 存
V put(K key, V value)
// 取
V get(Object key)
// 数量
int size()
// 删除
V remove(Object key)
// 包含测试
boolean containsKey(Object key)

3、HashMap构造方法


HashMap()
// initialCapacity 初始化大小
HashMap(int initialCapacity)
// loadFactor 负载因子
HashMap(int initialCapacity, float loadFactor)

4、HashMap基本用法


package com.demo.map;
import java.util.HashMap;
import java.util.Map;
public class MapDemo {
    public static void main(String[] args) {
        Map<String, Integer> userMap = new HashMap<>();
        userMap.put("Tom", 23);
        System.out.println(userMap.get("Tom"));
        // 23
    }
}

5、HashMap的Entry结构

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
}

6、使用示例

Map<String, Integer> userMap = new HashMap<>();
userMap.put("Tom", 21);
userMap.put("Jack", 22);
userMap.put("Steve", 23);
System.out.println(userMap.get("Tom"));
// 21
System.out.println(userMap);
// {Tom=21, Steve=23, Jack=22}

7、HashMap的遍历-keySet


获取key, 再通过key取到value


for (String key : userMap.keySet()) {
    System.out.println(key + ": " + userMap.get(key));
}
// Tom: 21
// Steve: 23
// Jack: 22

8、HashMap的遍历-values


只能获取value


for (Integer value : userMap.values()) {
    System.out.println(value);
}
// 21
// 23
// 22

9、HashMap的遍历-entrySet


获取Map.Entry 对象


for (Map.Entry<String, Integer> entry : userMap.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Tom: 21
// Steve: 23
// Jack: 22

10、HashMap的遍历-Iterator


import java.util.Iterator;
Iterator<Map.Entry<String, Integer>> iterator = userMap.entrySet().iterator();
while (iterator.hasNext()){
    Map.Entry<String, Integer> entry = iterator.next();
    System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Tom: 21
// Steve: 23
// Jack: 22

11、性能分析


完整代码

package com.demo.map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class MapDemo {
    public static void showMapByKeySet(Map<String, Integer> userMap) {
        long start = System.currentTimeMillis();
        Integer value;
        for (String key : userMap.keySet()) {
            // System.out.println(key + "=" + userMap.get(key));
            value = userMap.get(key);
        }
        long end = System.currentTimeMillis();
        System.out.println("keySet=" + (end - start));
    }
    public static void showMapByValues(Map<String, Integer> userMap) {
        long start = System.currentTimeMillis();
        Integer value;
        for (Integer v : userMap.values()) {
            // System.out.println(value);
            value = v;
        }
        long end = System.currentTimeMillis();
        System.out.println("values=" + (end - start));
    }
    public static void showMapByEntrySet(Map<String, Integer> userMap) {
        long start = System.currentTimeMillis();
        Integer value;
        for (Map.Entry<String, Integer> entry : userMap.entrySet()) {
            // System.out.println(entry.getKey() + ": " + entry.getValue());
            value = entry.getValue();
        }
        long end = System.currentTimeMillis();
        System.out.println("entrySet=" + (end - start));
    }
    public static void showMapByIterator(Map<String, Integer> userMap) {
        long start = System.currentTimeMillis();
        Iterator<Map.Entry<String, Integer>> iterator = userMap.entrySet().iterator();
        Integer value;
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            // System.out.println(entry.getKey() + ": " + entry.getValue());
             value = entry.getValue();
        }
        long end = System.currentTimeMillis();
        System.out.println("iterator=" + (end - start));
    }
    public static void main(String[] args) {
        Map<String, Integer> userMap = new HashMap<>();
        String[] keys = new String[]{
                "a", "b", "c", "d", "e",
                "f", "g", "h", "i", "j"
        };
        String key;
        for (int i = 0; i < 100000; i++) {
            // 让key接近真实环境
            key = keys[(int) (Math.random() * keys.length)] + i * 100;
            userMap.put(key, i);
        }
        showMapByKeySet(userMap); // keySet=16
        showMapByValues(userMap); // values=9
        showMapByEntrySet(userMap); // entrySet=10
        showMapByIterator(userMap); // iterator=9
    }
}

结论:


推荐 Iterator

避免 keySet

常用 EntrySet

12、HashMap示例


Map<String, String> user1 = new HashMap<>();
user1.put("name", "Tom");
user1.put("age", "23");
Map<String, String> user2 = new HashMap<>();
user2.put("name", "Jack");
user2.put("age", "24");
Map<String, String> user3 = new HashMap<>();
user3.put("name", "Steve");
user3.put("age", "25");
// 将数据放入Map
Map<String, Map> userMap = new HashMap<>();
userMap.put("Tom", user1);
userMap.put("Jack", user2);
userMap.put("Steve", user3);
System.out.println(userMap);
// {
//  Tom={name=Tom, age=23},
//  Steve={name=Steve, age=25},
//  Jack={name=Jack, age=24}
// }
// 将数据放入List
List<Map> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
userList.add(user3);
System.out.println(userList);
// [
//  {name=Tom, age=23},
//  {name=Jack, age=24},
//  {name=Steve, age=25}
// ]

HashMap底层原理

1、HashMap默认参数


初始化大小=16

负载因子=0.75 有效长度:16*0.75 = 12

扩容倍数=2

HashMap()
// 等同于
HashMap(16, 0.75f)

根据hash码取余数来决定位置


key是字符串类型

先使用hashCode()方法将key转换成hash码后并进行优化


对优化后的hash码进行取址,确定在HashMap中的位置


int indexFor(int h, int length)

数字key存放原理


初始大小 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
要存入的值 
120 % 16  = 8
37 % 16   = 5
61 % 16   = 13 
40 % 16   = 8
92 % 16   = 12
78 % 16   = 14
存放位置 0 1 2 3 4  5   6  7 8    9 10 11 12  13  14 15
                   37       120          92  61  78
                            40

验证代码


Map<Integer, String> map = new HashMap<>();
map.put(120, "120");
map.put(37, "37");
map.put(61, "61");
map.put(40, "40");
map.put(92, "92");
map.put(78, "78");
System.out.println(map);
//  {37=37, 120=120, 40=40, 92=92, 61=61, 78=78}

2、带参构造方法


HashMap(int initialCapacity, float loadFactor)
HashMap(int initialCapacity)
HashMap(5)

initialCapacity 初始化长度内部计算


// 1073741824
static final int MAXIMUM_CAPACITY = 1 << 30;
static final int tableSizeFor(int cap) {
    int n = cap - 1; // 4 => 100
    n |= n >>> 1;  // 100 | 010 => 110
    n |= n >>> 2;  // 110 | 001 => 111
    n |= n >>> 4;  // 111 | 000 => 111
    n |= n >>> 8;  // 111 | 000 => 111
    n |= n >>> 16; // 111 | 000 => 111
    // 111 => 7 + 1 => 8
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
// 取到大于5,最小的2的n次方
tableSizeFor(5) // 8

2倍扩容后重新计算位置


120 % 16 = 8   ->  120 % 32 = 24
37  % 16 = 5   ->  37  % 32 = 5
61  % 16 = 13  ->  61  % 32 = 29
40  % 16 = 8   ->  40  % 32 = 8
92  % 16 = 12  ->  92  % 32 = 28
78  % 16 = 14  ->  78  % 32 = 14

3、问题:


// 问题1:初始化长度是多少?
new HashMap(5) // 8
// 问题2:以下初始化后存入10000条数据,会发生扩容吗?
new HashMap(10000, 0.75f)
// 后台优化:2^14 = 16384 * 0.75 = 12288
// 所以,不会扩容

4、性能测试


package com.demo.map;
import java.util.HashMap;
import java.util.Map;
/**
 * 创建10个HashMap,每个HashMap添加1万条数据
 * 传递不同的构造参数,比较速度
 *
 * initialCapacity=16      avg=2318299
 * initialCapacity=10000   avg=1926625
 */
public class MapDemo {
    public static void main(String[] args) {
        long sum = 0L;
        for (int i = 0; i < 10; i++) {
            sum += inputMap(10000);
        }
        System.out.println("avg=" + (sum/10));
    }
    public static long inputMap(int initialCapacity) {
        Map<String, Object> map = new HashMap<>(initialCapacity);
        String key;
        // 获取纳秒
        long start = System.nanoTime();
        for (int i = 0; i < 10000; i++) {
            key = String.valueOf(i);
            map.put(key, "value");
        }
        long end = System.nanoTime();
        long timespan = end - start;
        System.out.println("timespan=" + timespan);
        return timespan;
    }
}

HashMap常用方法

添加元素put putIfAbsent

判断是否为空isEmpty,删除节点remove,清空clear

判断是否有key(containsKey),是否有value(containsValue)

替换某个key的value(replace, put)


代码实例


Map<String, Object> map = new HashMap<>();
// 添加数据
map.put("name", "Tom");
map.put("age", "12");
// 替换数据
map.put("name", "Jack");
// 替换数据
map.replace("name", "Steve");
//存在则不替换
map.putIfAbsent("name", "Seven");
// 移除数据
map.remove("age");
// 判断key存在,值存在
System.out.println(map.containsKey("name")); // true
System.out.println(map.containsValue("name")); // false
// 判空
System.out.println(map.isEmpty());
System.out.println(map);
// {name=Steve, age=12}
// 清空数据
map.clear();

forEach (Lambda表达式)


Map<String, Integer> map = new HashMap<>();
// 添加数据
map.put("a", 1);
map.put("b", 2);
map.forEach((key, value) -> {
    System.out.println(key + ": " + value);
});
// a: 1
// b: 2

getOrDefault获取默认值


Map<String, Integer> map = new HashMap<>();
Integer value = map.getOrDefault("a", 666);
System.out.println(value); // 666

LinkedHashMap

性能测试


数据量不同,性能表现也不一样

package com.demo.map;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class MapDemo {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        Map<String, Integer> linkedMap = new LinkedHashMap<>();
        long start, end;
        // 插入测试
        start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            map.put(String.valueOf(i), i);
        }
        end = System.currentTimeMillis();
        System.out.println("map put=" + (end - start));
        start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            linkedMap.put(String.valueOf(i), i);
        }
        end = System.currentTimeMillis();
        System.out.println("linkedMap put=" + (end - start));
        // 取出测试
        start = System.currentTimeMillis();
        for (Integer value : map.values()) {
        }
        end = System.currentTimeMillis();
        System.out.println("map get=" + (end - start));
        start = System.currentTimeMillis();
        for (Integer value : map.values()) {
        }
        end = System.currentTimeMillis();
        System.out.println("linkedMap get=" + (end - start));
        // map put = 29
        // linkedMap put = 22
        // map get = 6
        // linkedMap get = 3
    }
}

LinkedHashMap实现LRU(Least Recently Used)

LinkedHashMap有序


LRU实现


package com.demo.map;
import java.util.LinkedHashMap;
import java.util.Map;
public class LRUMap<K, V> extends LinkedHashMap<K, V> {
    private int maxSize;
    public LRUMap(int maxSize){
        super(16, 0.75f, true);
        this.maxSize = maxSize;
    }
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > this.maxSize;
    }
}

测试代码


package com.demo.map;
public class MapDemo {
    public static void main(String[] args) {
        LRUMap<String, Integer> lruMap = new LRUMap<>(3);
        lruMap.put("a", 1);
        lruMap.put("b", 2);
        lruMap.get("a");
        lruMap.put("c", 3);
        lruMap.put("d", 4);
        System.out.println(lruMap);
        //    {a=1, c=3, d=4}
    }
}

TreeMap

默认是key升序排序,可以自定义比较器Comparator


package com.demo.map;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class MapDemo {
    public static void main(String[] args) {
        Map<String, String> treeMap = new TreeMap<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        });
        treeMap.put("a", "a");
        treeMap.put("c", "c");
        treeMap.put("b", "b");
        System.out.println(treeMap);
        // 默认: {a=a, b=b, c=c}
        // 比较器排序后:{c=c, b=b, a=a}
    }
}

耗时对比

package com.demo.map;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
public class MapDemo {
    public static void main(String[] args) {
        Map<String, Integer> hashMap = new HashMap<>();
        Map<String, Integer> linkedMap = new LinkedHashMap<>();
        Map<String, Integer> treeMap = new TreeMap<>();
        long count = 1000;
        long start, end;
        // 插入测试
        start = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            hashMap.put(String.valueOf(i), i);
        }
        end = System.currentTimeMillis();
        System.out.println("map put=" + (end - start));
        start = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            linkedMap.put(String.valueOf(i), i);
        }
        end = System.currentTimeMillis();
        System.out.println("linkedMap put=" + (end - start));
        start = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            treeMap.put(String.valueOf(i), i);
        }
        end = System.currentTimeMillis();
        System.out.println("treeMap put=" + (end - start));
        // 取出测试
        start = System.currentTimeMillis();
        for (Integer value : hashMap.values()) {
        }
        end = System.currentTimeMillis();
        System.out.println("map get=" + (end - start));
        start = System.currentTimeMillis();
        for (Integer value : linkedMap.values()) {
        }
        end = System.currentTimeMillis();
        System.out.println("linkedMap get=" + (end - start));
        start = System.currentTimeMillis();
        for (Integer value : treeMap.values()) {
        }
        end = System.currentTimeMillis();
        System.out.println("treeMap get=" + (end - start));
    }
}

课程小结

Map接口的常见方法

对比不同的遍历方法,效率

底层原理,创建Map时,针对不同情况选择合适的构造方法

HashMap、LinkedMap、TreeMap区别与选择

相关文章
|
1月前
|
IDE Java 数据挖掘
Java 基础类从入门到精通实操指南
这份指南专注于**Java 17+**的新特性和基础类库的现代化用法,涵盖开发环境配置、数据类型增强(如文本块)、字符串与集合处理进阶、异常改进(如密封类)、IO操作及实战案例。通过具体代码示例,如CSV数据分析工具,帮助开发者掌握高效编程技巧。同时提供性能优化建议和常用第三方库推荐,适合从入门到精通的Java学习者。资源链接:[点此下载](https://pan.quark.cn/s/14fcf913bae6)。
119 36
|
22天前
|
Java API 微服务
2025 年 Java 从入门到精通学习笔记全新版
《Java学习笔记:从入门到精通(2025更新版)》是一本全面覆盖Java开发核心技能的指南,适合零基础到高级开发者。内容包括Java基础(如开发环境配置、核心语法增强)、面向对象编程(密封类、接口增强)、进阶技术(虚拟线程、结构化并发、向量API)、实用类库与框架(HTTP客户端、Spring Boot)、微服务与云原生(容器化、Kubernetes)、响应式编程(Reactor、WebFlux)、函数式编程(Stream API)、测试技术(JUnit 5、Mockito)、数据持久化(JPA、R2DBC)以及实战项目(Todo应用)。
82 5
|
1月前
|
监控 Java 测试技术
2025 年 Java 核心技术从入门到精通实战指南
《2025年Java核心技术实战指南》全面覆盖Java开发的最新趋势与最佳实践。内容包括Java新特性(如模式匹配、文本块、记录类)、微服务架构(Spring Boot 3.0+、Spring Cloud)、响应式编程(Reactor、WebFlux)、容器化与云原生(Docker、Kubernetes)、数据访问技术(JPA、R2DBC)、函数式编程、单元测试与集成测试(JUnit 5、Mockito)、性能优化与监控等。通过实战案例,帮助开发者掌握构建高性能、高可用系统的技能。代码资源可从[链接](https://pan.quark.cn/s/14fcf913bae6)获取。
121 7
|
1月前
|
消息中间件 Java 微服务
2025 版 Java 学习路线实战指南从入门到精通
《Java学习路线实战指南(2025版)》是一份全面的Java开发学习手册,涵盖基础环境搭建、核心语法与新特性、数据结构与算法、微服务架构、云原生技术栈、AI融合及项目实战。内容包括JDK安装配置、IntelliJ IDEA设置、Records类与模式匹配增强、LeetCode题解、Spring Cloud微服务开发、Kubernetes部署、OpenAI API调用等。结合在线商城系统案例,采用Vue 3、Spring Boot 3.5、MySQL、Elasticsearch等技术,提供从理论到实践的完整路径,助力开发者掌握2025年最新趋势与最佳实践。
157 4
|
19天前
|
Oracle Java 关系型数据库
java 入门学习视频_2025 最新 java 入门零基础学习视频教程
《Java 21 入门实操指南(2025年版)》提供了Java最新特性的开发指导。首先介绍了JDK 21和IntelliJ IDEA 2025.1的环境配置,包括环境变量设置和预览功能启用。重点讲解了Java 21三大核心特性:虚拟线程简化高并发编程,Record模式优化数据解构,字符串模板提升字符串拼接可读性。最后通过图书管理系统案例,展示如何运用Record定义实体类、使用Stream API进行数据操作,以及结合字符串模板实现控制台交互。该指南完整呈现了从环境搭建到实际项目开发的Java 21全流程实
47 1
|
23天前
|
算法 Java 测试技术
Java 从入门到实战完整学习路径与项目实战指南
本文详细介绍了“Java从入门到实战”的学习路径与应用实例,涵盖基础、进阶、框架工具及项目实战四个阶段。内容包括环境搭建、语法基础、面向对象编程,数据结构与算法、多线程并发、JVM原理,以及Spring框架等核心技术。通过学生管理系统、文件下载器和博客系统等实例,帮助读者将理论应用于实践。最后,提供全链路电商系统的开发方案,涉及前后端技术栈与分布式架构。附代码资源链接,助力成为合格的Java开发者。
50 4
|
28天前
|
监控 Java API
Java 异步编程难题拆解实操指南:从入门到精通解决异步编程关键问题
本文深入探讨了Java异步编程的实操技巧,基于Project Reactor与Spring WebFlux等技术框架,通过具体案例解决常见难题。内容涵盖反应式编程基础、回调地狱解决方案、并行任务处理、响应式REST API开发、背压策略应用、微服务异步通信及性能监控等方面。结合代码示例,详细讲解了如何构建高性能异步应用,并总结了最佳实践,帮助开发者掌握异步编程的核心技能。适合希望提升异步开发能力的技术人员阅读。
42 3
|
1月前
|
前端开发 Java 微服务
2025 年全网超全 Java 从入门到精通学习路线指南
这是一份全面的Java学习路线图,涵盖从基础到进阶的知识体系。基础阶段包括环境搭建、语法学习与面向对象编程;进阶阶段深入数据结构、多线程、JVM原理及泛型集合;框架阶段掌握Spring、MyBatis等工具;数据库阶段学习SQL、MySQL及Redis;前端技术涉及HTML、CSS与Vue;分布式阶段探讨微服务架构、Docker与Kubernetes;最后通过企业级项目实战提升性能优化与代码重构能力。资源地址:[https://pan.quark.cn/s/14fcf913bae6](https://pan.quark.cn/s/14fcf913bae6)
465 7
|
23天前
|
Java API 微服务
Java 21 与 Spring Boot 3.2 微服务开发从入门到精通实操指南
《Java 21与Spring Boot 3.2微服务开发实践》摘要: 本文基于Java 21和Spring Boot 3.2最新特性,通过完整代码示例展示了微服务开发全流程。主要内容包括:1) 使用Spring Initializr初始化项目,集成Web、JPA、H2等组件;2) 配置虚拟线程支持高并发;3) 采用记录类优化DTO设计;4) 实现JPA Repository与Stream API数据访问;5) 服务层整合虚拟线程异步处理和结构化并发;6) 构建RESTful API并使用Springdoc生成文档。文中特别演示了虚拟线程配置(@Async)和StructuredTaskSco
85 0
|
23天前
|
Cloud Native Java 微服务
最新 Java 从入门到实战技术实操指南
这是一份全面的Java实操指南,涵盖从入门到微服务架构的完整学习路径。内容包括Java 21新特性(虚拟线程、Record类)、响应式编程(Spring WebFlux)、微服务架构(Spring Boot 3.2、Spring Cloud、Kubernetes)、数据库与缓存(Redis 8、R2DBC)以及云原生部署和监控(Prometheus、Grafana)。通过电商系统实战项目,掌握最新技术栈与开发技巧。适合初学者及进阶开发者,附带代码示例与资源链接,助你快速提升技能。
40 0

热门文章

最新文章