Java基础二十五(Map)

这篇具有很好参考价值的文章主要介绍了Java基础二十五(Map)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Map 接口

Map 接口是 Java 集合框架中的一种用于储存键值对映射关系的接口。Map 接口提供了一种通过键来访问值的方式,其中每一个键都是唯一的,值可以重复。

public interface Map<K,V>

Map 接口的主要特征如下:

  1. 键唯一性:Map 中的键是唯一的,同一个键只能对应一个值。如果重复插入相同的键,则后面的值会覆盖前面的值。

  2. 键值对映射:Map 中的每个键都与一个值相关联,它们之间形成了键值对的映射关系。通过键可以获取对应的值。

  3. 无序性:Map 不保证元素的顺序,即不按照添加的顺序保存键值对。如果需要有序遍历,可以使用 SortedMap 的实现类(如 TreeMap)或将 Map 转换为有序的集合。

  4. 允许空键和空值:Map 中可以存储空键(null)和空值(null),但是需要注意空键只能有一个,因为键是唯一的。

  5. 动态调整大小:Map 实现可以根据需要自动调整其内部存储的大小,以容纳更多的键值对。

  6. 遍历操作:Map 提供了多种遍历键、值或键值对的方式,如使用迭代器、keySetvaluesentrySet 等方法。

  7. 实现类:Java 提供了多个 Map 的实现类,常用的有 HashMapTreeMapLinkedHashMap 等,每个实现类都有其特定的性能和用途。

Map 接口常用的方法:

方法 返回值 描述
clear() void 删除所有的映射
containsKey(Object key) boolean Map中是否有这个key
containsValue(Object value) boolean Map是否有这个value
entrySet() Set<Map.Entry<K,V>> 返回包含的映射的Set
get(Object key) V 根据key返回对应的value
isEmpty() boolean Map是否为空
keySet() Set 返回Map中所有key的Set
put(K key, V value) V 向Map添加映射
putAll(Map<? extends K,? extends V> m) void 将指定Map中的映射复制到此映射
remove(Object key) V 如果key存在,删除映射
remove(Object key, Object value) boolean 当key、value映射存在时,删除
replace(K key, V value) boolean 当key存在时,替换内容
size() int Map中映射的数量
values() Collection 返回所有value的集合

说明:

  • Map 接口是键值对映射表的接口,具有键和值的一一映射关系。
  • Map 中不能有重复的键,每个键只能映射唯一的值。
  • 当使用对象作为键时,需要重写 equalshashCode 方法。
  • Map 提供了多个操作方法,如判断是否包含指定键或值、添加、删除映射等。
  • entrySet() 方法返回一个包含所有键值映射的 Set 集合。
  • keySet() 方法返回一个包含所有键的 Set 集合。
  • values() 方法返回一个包含所有值的集合。

JDK9 提供了创建不可修改 Map 对象的方法:

  1. Map.of
  2. Map.ofEntries
  3. Map.copyOf

1. HashMap

public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, Serializable

HashMapMap 接口的一个实现类,它使用哈希表来存储键值对。

特点:

  • HashMap 允许键和值为null
  • 键不允许重复,值可以重复,但是每个键只能映射到一个值。
  • 查找、插入和删除操作的时间复杂度接近常数时间(O(1))
  • 非线程安全
  • 默认容量 16,默认负载因子 0.75,扩容是 2 倍旧的容量
    • 默认的初始容量为16,意味着在创建 HashMap 对象时,底层会初始化一个长度为16的数组用于存储键值对。
    • 负载因子(load factor)是一个用于控制 HashMap 扩容的参数。默认的负载因子为0.75。负载因子表示当哈希表中的键值对数量达到容量与负载因子乘积的阈值时,就会进行扩容。例如,在默认情况下,当哈希表中的键值对数量达到了 16 * 0.75 = 12 时,就会进行扩容操作。
  • 在存储数据时, keyhash 计算调用的是 HashMap 中的 hash 方法
  • 内部采用 数组 + 链表实现 , JDK 8 及以后版本增加红黑树的支持。

常用方法:

  • put(key, value):向 HashMap 中添加一个键值对。
  • get(key):根据键获取对应的值。
  • remove(key):根据键删除对应的键值对。
  • containsKey(key):判断是否包含指定的键。
  • containsValue(value):判断是否包含指定的值。
  • size():返回 HashMap 中键值对的数量。
  • clear():清空 HashMap 中的所有键值对。

示例代码:文章来源地址https://www.toymoban.com/news/detail-697629.html

import java.util.HashMap;

public class HashMapExample {
    public static void main(String[] args) {
        // 创建一个HashMap对象
        HashMap<String, Integer> hashMap = new HashMap<>();

        // 添加键值对
        hashMap.put("apple", 1);
        hashMap.put("banana", 2);
        hashMap.put("orange", 3);

        // 获取值
        int value = hashMap.get("apple");
        System.out.println("Value for key 'apple': " + value);

        // 判断是否包含指定键
        boolean containsKey = hashMap.containsKey("banana");
        System.out.println("Contains key 'banana': " + containsKey);

        // 删除键值对
        hashMap.remove("orange");

        // 获取键值对数量
        int size = hashMap.size();
        System.out.println("Size of HashMap: " + size);

        // 清空HashMap
        hashMap.clear();
        System.out.println("HashMap is empty: " + hashMap.isEmpty());
    }
}

请注意,本接口不保证映射的顺序,如果需要按特定顺序遍历键值对,可以使用 LinkedHashMap 类来代替。

构造方法
HashMap 类提供了多个构造方法,下面列出了常用的几种:

  1. HashMap():创建一个空的 HashMap 对象,初始容量为16,加载因子为0.75。
  2. HashMap(int initialCapacity):创建一个空的 HashMap 对象,指定初始容量,加载因子为0.75。
  3. HashMap(int initialCapacity, float loadFactor):创建一个空的 HashMap 对象,指定初始容量和加载因子。
  4. HashMap(Map<? extends K,? extends V> m):创建一个新的 HashMap 对象,将指定 Map 中所有键值对复制到创建的 HashMap 中。

示例代码:

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

public class HashMapExample {
    public static void main(String[] args) {
        // 创建一个空的HashMap对象,默认初始容量为16,加载因子为0.75
        HashMap<String, Integer> hashMap1 = new HashMap<>();

        // 创建一个初始容量为10的HashMap对象,加载因子为0.75
        HashMap<String, Integer> hashMap2 = new HashMap<>(10);

        // 创建一个初始容量为10,加载因子为0.5的HashMap对象
        HashMap<String, Integer> hashMap3 = new HashMap<>(10, 0.5f);

        // 创建一个包含键值对的Map对象
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 1);
        map.put("banana", 2);
        map.put("orange", 3);

        // 将map中的键值对复制到HashMap对象中
        HashMap<String, Integer> hashMap4 = new HashMap<>(map);
    }
}

HashMap 中 put 过程
Java基础二十五(Map),Java,java,开发语言

HashMapput(key, value) 方法用于将指定的键值对插入到哈希表中。下面是 put(key, value) 方法的简化实现过程:

  1. 判断是否需要进行扩容操作:

    • 如果当前键值对数量超过了容量与负载因子乘积的阈值,就会进行扩容操作。
    • 扩容操作会创建一个新的更大的数组,并重新计算每个键的哈希值,将键值对重新散列到新的数组中。
  2. 计算键的哈希值:

    • 使用键对象的 hashCode() 方法计算哈希值。
    • 这个哈希值会通过一系列位运算来得到最终的存储位置。
  3. 查找存储位置:

    • 根据计算得到的哈希值,找到在数组中对应的索引位置。
  4. 处理冲突:

    • 如果发生了哈希冲突,即多个键的哈希值相同,此时会使用链表或红黑树等数据结构来存储冲突的键值对。
    • 如果链表长度较长(默认为8,以链表的形式放到后边,长度超过阈值且数组长度大于等于64),会将链表转换为红黑树,以提高查找效率。删除元素时,如果时以红黑树存储的如果节点小于 6 个将会变为链表存储
  5. 插入键值对:

    • 如果找到了对应的存储位置,直接插入键值对。
    • 如果存在相同的键,则用新的值替换旧的值。

示例代码:

public V put(K key, V value) {
    // 判断是否需要扩容
    if (size + 1 > threshold) {
        resize();
    }

    // 计算键的哈希值
    int hash = hash(key);

    // 查找存储位置
    int index = indexFor(hash, table.length);

    // 处理冲突
    for (Entry<K,V> e = table[index]; e != null; e = e.next) {
        if (e.hash == hash && (key == e.key || key.equals(e.key))) {
            V oldValue = e.value;
            e.value = value;
            return oldValue; // 返回旧值
        }
    }

    // 插入新的键值对
    addEntry(hash, key, value, index);
    return null;
}

这只是 put(key, value) 方法的简化实现,实际上还有更多细节和边界情况需要考虑。以上简化的代码只是为了帮助理解 put 操作的基本原理。实际的 HashMap 类中的代码更加复杂,涉及更多细节和优化。

2. TreeMap

public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, Serializable

TreeMap 是 Java 中的一种有序映射(SortedMap)实现,它基于红黑树(Red-Black Tree)数据结构来存储键值对,并保持键的有序性
Java基础二十五(Map),Java,java,开发语言

特点:

  1. 键的有序性:TreeMap 会根据键的自然顺序或自定义比较器来对键进行排序。这使得 TreeMap 的键值对在迭代时可以按照键的顺序访问。

  2. 动态维护有序:当插入、删除或查询操作时,TreeMap 内部会自动调整红黑树的结构来保持键的有序性。因此,在任何时候,TreeMap 的键都是有序的。

  3. 高效的遍历操作:由于 TreeMap 的键是有序的,因此可以根据键的范围进行快速的查找和遍历操作。例如,可以使用 subMap() 方法获取指定范围内的子映射。

  4. 支持自定义排序规则:通过提供自定义的比较器(Comparator),可以在创建 TreeMap 对象时指定键的排序方式。如果没有提供比较器,则使用键的自然顺序(实现了 Comparable 接口的键)。

  5. 非线程安全

  6. 允许 null 值,当 null 作为键时,由于键是有序的,需要使用自定义比较器。

示例代码:
以下是一个示例,演示如何使用自定义比较器来处理 null 键:

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

public class TreeMapExample {
    public static void main(String[] args) {
        // 自定义比较器,处理 null 键
        Comparator<String> nullComparator = new Comparator<String>() {
            @Override
            public int compare(String str1, String str2) {
                if (str1 == null && str2 == null) {
                    return 0;
                } else if (str1 == null) {
                    return -1;
                } else if (str2 == null) {
                    return 1;
                } else {
                    return str1.compareTo(str2);
                }
            }
        };

        // 使用自定义比较器创建 TreeMap
        TreeMap<String, Integer> treeMap = new TreeMap<>(nullComparator);

        treeMap.put(null, 1);
        treeMap.put("apple", 2);
        treeMap.put("banana", 3);
        treeMap.put("tangerine", 4);

        for (Map.Entry<String, Integer> entry : treeMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // 获取第一个和最后一个键
        System.out.println(treeMap.firstKey());  // null
        System.out.println(treeMap.lastKey());  // banana
        System.out.println(treeMap.firstEntry());  // null=1
        System.out.println(treeMap.lastEntry());  // banana=3

        // 获取小于等于指定键的最大值和大于等于指定键的最小值
        System.out.println(treeMap.floorKey("c"));  // banana
        System.out.println(treeMap.ceilingKey("c"));  // tangerine
        System.out.println(treeMap.floorEntry("c"));  // banana=3
        System.out.println(treeMap.ceilingEntry("c"));  // tangerine=4

        // 按照倒叙输出
        System.out.println(treeMap.descendingMap());  // {tangerine=4, banana=3, apple=2, null=1}
        System.out.println(treeMap);  // {null=1, apple=2, banana=3, tangerine=4}
        System.out.println(treeMap.descendingKeySet());  // [tangerine, banana, apple, null]
    }
}

在这个示例中,我们创建了一个自定义比较器 nullComparator 来处理 null 键。它首先检查两个键是否都是 null,然后再处理其他情况。这样,即使有 null 键存在,TreeMap 也能够正确地进行排序并保持有序状态。

3. Hashtable

public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, Serializable

Hashtable 是 Java 中的一种哈希表数据结构,它实现了 Map 接口,提供了键值对的储存和检索功能。

特点:

  1. 线程安全(同步的)
  2. 键值不允许为null
  3. 动态扩容:默认初始容量( initialCapacity )为 11 ,默认负载因子( loadFactor )为 0.75f,扩容方式是旧容量的2倍 +1
    • 为了均匀分布,降低冲突率
  4. 不保证顺序
  5. 数组 + 链表方式存储实现Hash表存储
  6. 添加元素:
    • 如果 hash一样 equalsfalse 则将当前值添加到链表头
    • 如果 hash 一样 equalstrue 则使用当前值替换原来的值 ( key 相同)

Java基础二十五(Map),Java,java,开发语言

构造方法

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

HashMap 与 Hashtable 中 put 过程比较

  1. 线程安全性:Hashtable 是线程安全的,而 HashMap 不是。Hashtable 的所有方法都是同步的,可以在多线程环境下安全使用,但同时也会带来一定的性能开销。而 HashMap 不提供线程安全的保证,需要在多线程环境下自行处理同步。

  2. 允许键或值为空:Hashtable 不允许键或值为空(null),如果尝试插入空键或值,会抛出 NullPointerException。而 HashMap 允许键和值都为空。

  3. 继承关系:Hashtable 是早期的 Java 类,实现了 Dictionary 接口,并继承自 Dictionary 类。而 HashMap 是 Java Collections Framework 的一部分,实现了 Map 接口,并继承自 AbstractMap 类。

  4. 同步策略:Hashtable 使用 synchronized 关键字来实现线程安全。而 HashMap 在默认情况下不进行同步,可以通过 Collections.synchronizedMap 方法将 HashMap 转换为线程安全的版本。

在 put 过程中,Hashtable 和 HashMap 的基本逻辑是相同的,都是根据键的哈希码计算索引位置,解决冲突,并插入键值对。区别主要体现在线程安全性和对空键或值的处理上。因此,在单线程环境下,如果不需要线程安全性,推荐使用 HashMap,它具有更好的性能。

示例代码:

import java.util.Hashtable;

public class HashtableExample {
    public static void main(String[] args) {
        Hashtable<String, Integer> hashtable = new Hashtable<>();
        // 添加键值对
        hashtable.put("one", 1);
        hashtable.put("two", 2);
        hashtable.put("three", 3);
        hashtable.put("four", 4);

        // 获取键值对
        System.out.println(hashtable.get("one"));  // 1
        // getOrDefault获取值
        System.out.println(hashtable.getOrDefault("one", 2));  // 1
        System.out.println(hashtable);  // {two=2, one=1, three=3, four=4}

        // 判断是否包含指定的值或键
        System.out.println(hashtable.contains(4));  // true,是否包含值
        System.out.println(hashtable.containsKey("one"));  // true,是否包含键
        System.out.println(hashtable.containsValue(4));  // true,是否包含值

        // 遍历键值对
        for (String key : hashtable.keySet()) {
            System.out.println(key + " : " + hashtable.get(key));
        }
    }
}

到了这里,关于Java基础二十五(Map)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 从零开始学习 Java:简单易懂的入门指南之Map集合(二十三)

    1.1Map集合概述和特点 Map集合概述 Map集合的特点 双列集合,一个键对应一个值 键不可以重复,值可以重复 Map集合的基本使用 1.2Map集合的基本功能 方法介绍 方法名 说明 V put(K key,V value) 添加元素 V remove(Object key) 根据键删除键值对元素 void clear() 移除所有的键值对元素 boolean con

    2024年02月09日
    浏览(35)
  • 【Java基础教程】(二十六)Java新特性篇 · 第六讲:注解——解码程序设计中的元数据利器,用小小的@符里做大大的开发~

    Java 注解(Annotation) 是Java语言中一种元数据形式,它提供了一种在代码中添加元数据的方式。注解为程序员提供了向代码中添加额外信息的能力,这些额外信息可以被编译器、工具或者运行时环境使用。 优点: 提供了一种 更加简洁和可读性强 的代码编写风格; 增强代码的

    2024年02月16日
    浏览(28)
  • C语言学习(二十五)---指针练习题(一)

    在上一节内容中,我们学习了 递归 与 冒泡排序法 的有关内容,今天我们将继续往下学习,主要内容为 指针练习题 ,好了,话不多说,开整!!! 在之前的第18—22的内容中,我们学习了 指针 的一系列内容,今天我们对之前的 指针内容进行测试 ,在查看答案之前,请先自

    2024年02月10日
    浏览(30)
  • 【Java 基础篇】Java Map 详解

    在Java的集合框架中, Map 接口用于存储键值对,提供了一种基于键进行查找和操作的数据结构。 Map 接口的实现类提供了丰富的方法来操作键值对,例如添加、删除、更新和查找。本文将详细介绍Java中的 Map 接口及其常见实现类,包括 HashMap 、 TreeMap 和 LinkedHashMap ,并提供一

    2024年02月11日
    浏览(33)
  • 第四十一天 Java基础学习(三十五)

    一、JSP内置对象 ●内置对象 因为SP的本质是Servlet,在JSP文件经过转译之后,生成JAVA代码,在运行时JSP给我们准备好了九个可以直接使用而不用我们自己去new的对象,这九个对象我之为内置对象. 内置对象完全由SP自行去维护,我们直接使用即可。 ●九大内置对象 confia ;page ;

    2024年02月16日
    浏览(29)
  • java基础——Map

    Java提供了专门的集合类用来存放键值对关系的对象,即 java.util.Map 接口。 我们通过查看 Map 接口描述,发现 Map 接口下的集合与 Collection 接口下的集合,它们存储数据的形式不同,如下图。 Collection 中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个

    2024年02月03日
    浏览(26)
  • 【Java基础】Java 8中Map的遍历方式

    Java 8引入了许多新特性,其中包括对集合的处理方式进行了重大升级。对于Map类型,Java 8也提供了一些新的遍历方式,能够更加优雅地实现Map的遍历和操作。本文将介绍Java 8中常用的Map遍历方式,并比较它们之间的优缺点。 Java 8中新增了 forEach() 方法,可以通过lambda表达式或

    2023年04月22日
    浏览(54)
  • 【随笔】Git 基础篇 -- 远程仓库 git clone(二十五)

    💌 所属专栏:【Git】 😀 作  者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! 💖 欢迎大家:这里是CSDN,我总结知识的地方,喜欢的话请三连,有问题请私信 😘 😘 😘 您的点赞、关注、收藏、评论,是对我最大

    2024年04月15日
    浏览(37)
  • Java基础——Map集合遍历方式

    (1)方式一:键找值 先获取Map集合的全部键的Set集合。 遍历键的Set集合,然后通过键提取对应值。 涉及API: 方法名称 说明 SetK keySet() 获取所有键的集合 V get(Object key) 根据键获取值 (2)方式二:键值对 先把Map集合转换成Set集合,Set集合中每个元素都是键值对实现类型了。

    2023年04月10日
    浏览(31)
  • C语言第二十五弹---字符函数和字符串函数(上)

      ✨ 个人主页:   熬夜学编程的小林 💗 系列专栏:   【C语言详解】   【数据结构详解】 目录 1、字符分类函数 2、字符转换函数 3、strlen的使用和模拟实现 4、strcpy 的模拟实现 5、strcat 的模拟实现 6、strcmp 的模拟实现 7、strncpy 函数的使用 总结 在编程的过程中,我们经常

    2024年02月19日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包