首页 > 编程语言 >深入理解 Java 双列集合:Map 家族的探索与实践

深入理解 Java 双列集合:Map 家族的探索与实践

时间:2025-01-19 16:29:16浏览次数:3  
标签:Map Java map System println 双列 集合 out

在 Java 编程的世界里,集合框架是组织和操作数据的强大工具。其中,双列集合以独特的键值对存储方式,为我们处理数据提供了别样的思路。本文将深入探讨 Java 双列集合的核心概念、常见实现类及其应用场景。

双列集合的基本特性

双列集合,区别于单列集合,它一次存储一对数据,即键(Key)和值(Value)。这一特性使得数据的关联存储和快速检索成为可能。

  1. 键的唯一性:键在集合中不能重复,这是双列集合的基本约束。每个键如同一个独特的标识符,确保数据的准确性和高效查找。
  2. 值的多样性:与键不同,值可以重复。这为存储和管理复杂数据提供了灵活性,例如,一个键可以对应多个相同的值,或者不同键对应相同的值。
  3. 一一对应关系:键和值之间是严格的一一对应关系。通过键,我们可以迅速定位到其对应的值,这种映射关系是双列集合的核心机制。
  4. 键值对对象:在 Java 中,键和值的组合被称为 “键值对” 或 “Entry 对象”。它封装了键值之间的关系,是双列集合操作的基本单元。

Map 接口:双列集合的基石

Map是双列集合的顶层接口,定义了一系列操作键值对的方法,所有具体的双列集合实现类都继承了这些方法。

常见 API 方法详解

  1. V put(K key, V value):用于向集合中添加元素。如果键不存在,会将新的键值对添加到集合中,并返回null;若键已存在,则会用新的值覆盖旧值,并返回被覆盖的值。
  2. V remove(Object key):根据指定的键删除对应的键值对元素,并返回被删除的值。如果键不存在,返回null
  3. void clear():移除集合中的所有键值对元素,使集合变为空集。
  4. boolean containKey(Object Key):判断集合是否包含指定的键,返回truefalse,用于快速检查键的存在性。
  5. boolean containValue(Object value):判断集合是否包含指定的值,由于值可能重复,查找效率相对较低。
  6. boolean isEmpty():判断集合是否为空,即集合中是否不包含任何键值对。
  7. int size():返回集合中键值对的个数,即集合的长度。
import java.util.HashMap;
import java.util.Map;

public class MapAPIDemo {
    public static void main(String[] args) {
        // 创建Map集合的对象
        Map<String, String> m = new HashMap<>();
        // 添加元素
        m.put("喜羊羊", "美羊羊");
        m.put("智羊羊", "丽羊羊");
        m.put("灰太狼", "红太狼");

        // 演示put方法的覆盖功能
        String value2 = m.put("喜羊羊", "暖羊羊");
        System.out.println("被覆盖的值: " + value2); 

        // 删除元素
        String result = m.remove("喜羊羊");
        System.out.println("删除的值: " + result); 

        // 清空集合
        m.clear();
        System.out.println("清空后的集合: " + m); 

        // 判断是否包含键和值
        boolean keyResult = m.containsKey("喜羊羊");
        System.out.println("是否包含键'喜羊羊': " + keyResult); 

        boolean valueResult = m.containsValue("喜羊羊2");
        System.out.println("是否包含值'喜羊羊2': " + valueResult); 

        // 判断集合是否为空
        boolean isEmptyResult = m.isEmpty();
        System.out.println("集合是否为空: " + isEmptyResult); 

        // 获取集合长度
        int size = m.size();
        System.out.println("集合长度: " + size); 
    }
}

Map 的遍历方式

遍历 Map 集合是日常编程中常见的操作,Java 提供了多种方式来实现这一需求。

键找值方式

  1. 获取键集:首先通过keySet()方法获取 Map 中所有键的集合,该集合是一个单列集合。
  2. 遍历键集:使用for - each循环、迭代器或forEach方法遍历键集。
  3. 获取对应值:在遍历过程中,通过键调用get(key)方法获取对应的值。
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MapTraversalByKey {
    public static void main(String[] args) {
        // 创建Map集合的对象
        Map<String, String> map = new HashMap<>();
        map.put("喜羊羊", "美羊羊");
        map.put("智羊羊", "丽羊羊");
        map.put("灰太狼", "红太狼");

        // 获取所有的键
        Set<String> keys = map.keySet();

        // 使用for - each循环遍历
        for (String key : keys) {
            String value = map.get(key);
            System.out.println(key + "=" + value);
        }

        System.out.println("____________________________");

        // 使用迭代器遍历
        Iterator<String> it = keys.iterator();
        while (it.hasNext()) {
            String next = it.next();
            String value = map.get(next);
            System.out.println(next + "=" + value);
        }

        System.out.println("____________________________");

        // 使用forEach方法遍历
        keys.forEach(s -> {
            String value = map.get(s);
            System.out.println(s + "=" + value);
        });
    }
}

键值对方式

  1. 获取键值对集:通过entrySet()方法获取 Map 中所有键值对的集合,每个元素都是一个Entry对象。
  2. 遍历键值对集:同样可以使用for - each循环、迭代器或forEach方法遍历。
  3. 获取键和值:从Entry对象中分别调用getKey()getValue()方法获取键和值。
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MapTraversalByEntry {
    public static void main(String[] args) {
        // 创建Map集合的对象
        Map<String, String> map = new HashMap<>();
        map.put("喜羊羊", "美羊羊");
        map.put("智羊羊", "丽羊羊");
        map.put("灰太狼", "红太狼");

        // 获取所有的键值对
        Set<Map.Entry<String, String>> entries = map.entrySet();

        // 使用for - each循环遍历
        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "=" + value);
        }

        System.out.println("___________________________");

        // 使用迭代器遍历
        Iterator<Map.Entry<String, String>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> next = iterator.next();
            System.out.println(next);
        }

        System.out.println("--------------------------");

        // 使用forEach方法遍历
        entries.forEach(stringStringEntry -> System.out.println(stringStringEntry));
    }
}

Lambda 表达式方式

Map接口提供了forEach方法,结合 Lambda 表达式可以简洁地遍历 Map。

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;

public class MapTraversalWithLambda {
    public static void main(String[] args) {
        // 创建Map集合的对象
        Map<String, String> map = new HashMap<>();
        map.put("喜羊羊", "美羊羊");
        map.put("智羊羊", "丽羊羊");
        map.put("灰太狼", "红太狼");

        // 使用Lambda表达式遍历
        map.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System.out.println(key + "=" + value);
            }
        });

        System.out.println("_______________________________");

        // 使用更简洁的Lambda表达式
        map.forEach((key, value) -> System.out.println(key + "=" + value));
    }
}

常见的 Map 实现类

HashMap

  1. 基本特性HashMapMap接口最常用的实现类,它基于哈希表结构实现。
  2. 性能特点:具有出色的插入和查找性能,时间复杂度通常为 O (1)。这得益于其哈希表的设计,通过哈希函数将键映射到特定的存储位置。
  3. 键的唯一性保证:依赖hashCode方法和equals方法来保证键的唯一性。当向HashMap中添加键值对时,首先根据键的hashCode值计算存储位置,如果不同键的hashCode值相同(即哈希冲突),则通过equals方法进一步比较键的内容是否相同。如果键是自定义对象,必须重写hashCodeequals方法以确保正确的唯一性判断。
  4. 值的处理:对于值,HashMap不关心其唯一性,因此无需重写hashCodeequals方法(除非在特定业务逻辑中有此需求)。

LinkedHashMap

  1. 继承关系LinkedHashMap继承自HashMap,在保留哈希表特性的基础上,增加了对元素顺序的维护。
  2. 有序性实现:通过双向链表记录键值对的插入顺序,因此保证了存储和取出的元素顺序一致。这种特性在需要按插入顺序处理数据的场景中非常有用,例如缓存系统中需要按照访问顺序淘汰数据。
  3. 性能影响:由于额外维护了链表结构,LinkedHashMap的插入和删除操作性能略低于HashMap,但查找性能基本相同。

TreeMap

  1. 底层结构TreeMap基于红黑树结构实现,红黑树是一种自平衡的二叉搜索树。
  2. 排序特性:由键决定其特性,键不重复、无索引且可排序。默认情况下,TreeMap按照键的自然顺序(即实现了Comparable接口的顺序)进行排序。也可以通过在创建集合时传递Comparator比较器对象来自定义排序规则。
  3. 应用场景:适用于需要对键进行排序的场景,例如统计单词出现频率并按字母顺序输出。
自定义排序规则示例
  1. 实现 Comparable 接口:让自定义类实现Comparable接口,在compareTo方法中定义比较规则。
import java.util.TreeMap;

class Student implements Comparable<Student> {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public int compareTo(Student other) {
        // 按年龄升序排序
        return this.age - other.age;
    }
}

public class TreeMapSortByComparable {
    public static void main(String[] args) {
        TreeMap<Student, String> map = new TreeMap<>();
        map.put(new Student("Alice", 20), "A");
        map.put(new Student("Bob", 18), "B");
        map.put(new Student("Charlie", 22), "C");

        map.forEach((student, value) -> System.out.println(student.getName() + "(" + student.getAge() + ")=" + value));
    }
}
  1. 使用 Comparator 比较器:创建TreeMap时传递Comparator对象,在compare方法中定义比较规则。
import java.util.Comparator;
import java.util.TreeMap;

class Student {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class TreeMapSortByComparator {
    public static void main(String[] args) {
        TreeMap<Student, String> map = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                // 按姓名降序排序
                return s2.getName().compareTo(s1.getName());
            }
        });

        map.put(new Student("Alice", 20), "A");
        map.put(new Student("Bob", 18), "B");
        map.put(new Student("Charlie", 22), "C");

        map.forEach((student, value) -> System.out.println(student.getName() + "(" + student.getAge() + ")=" + value));
    }
}

新的统计思想:利用 Map 进行数据统计

在实际编程中,经常需要统计数据出现的次数。利用Map可以轻松实现这一功能。

统计字符出现次数示例

import java.util.StringJoiner;
import java.util.TreeMap;

public class CharacterCounting {
    public static void main(String[] args) {
        String s = "aababcabcdabcde";
        TreeMap<Character, Integer> tm = new TreeMap<>();

        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (tm.containsKey(c)) {
                int count = tm.get(c);
                count++;
                tm.put(c, count);
            } else {
                tm.put(c, 1);
            }
        }

        System.out.println(tm);

        StringBuilder sb = new StringBuilder();
        tm.forEach((key, value) -> sb.append(key).append("(").append(value).append(")"));
        System.out.println(sb);

        StringJoiner sj = new StringJoiner("", "", "");
        tm.forEach((key, value) -> sj.add(key + "").add("(").add(value + "").add(")"));
        System.out.println(sj);
    }
}

在这个例子中,我们使用TreeMap来统计字符串中每个字符出现的次数。键表示字符,值表示出现的次数。如果不需要对结果进行排序,使用HashMap会有更高的效率。

如何选择合适的双列集合

  1. 默认选择:如果没有特殊需求,HashMap通常是最佳选择,因为它具有最高的效率,适用于大多数需要快速插入和查找的场景。
  2. 有序性需求:当需要保证存储和取出的元素顺序一致时,应选择LinkedHashMap。例如,在实现一个需要按访问顺序缓存数据的系统中,LinkedHashMap可以方便地实现这一功能。
  3. 排序需求:如果需要对键进行排序,无论是自然顺序还是自定义顺序,TreeMap都是首选。比如在统计单词频率并按字母顺序输出的场景中,TreeMap能很好地满足需求。

通过深入理解双列集合的特性、常见实现类及其应用场景,我们能够在 Java 编程中更加高效、灵活地处理数据,提升程序的性能和可读性。希望本文能为你在集合框架的学习和应用中提供有价值的参考。

标签:Map,Java,map,System,println,双列,集合,out
From: https://blog.csdn.net/2201_75813105/article/details/145240341

相关文章

  • 偷偷的学Java
    序章:为何要偷偷学Java?•Java,不仅仅是一种编程语言• 偷偷学Java,快速提升你的竞争力•Java学习秘籍第一章:Java的神秘面纱•Java的起源与发展历程•Java的生态系统与应用场景•Java与其他编程语言的比较第二章:搭建你的Java秘密基地•安装Java开发工具包(JDK):不被发现......
  • java变量及八大基本数据类型的定义
    变量变量是什么:就是可以变化的量!java是一种强类型语言,每个变量都必须声明其类型java变量是程序中最基本的存储单元,其中要素包括变量名,变量类型,作用域注意事项每个变量都有类型,类型可以是基本类型,也可以是引用类型变量名必须是合法的标识符变量声明是一条完整的语句,因此......
  • 【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
    【华为OD-E卷-第k个排列100分(python、java、c++、js、c)】题目给定参数n,从1到n会有n个整数:1,2,3,…,n,这n个数字共有n!种排列。按大小顺序升序列出所有排列的情况,并一一标记,当n=3时,所有排列如下:“123”“132”“213”“231”“312”“321”给......
  • 咱们继续学Java——高级篇 第一百八十三篇:之Java高级Swing编程之JEditorPane组件与进
    咱们继续学Java——高级篇第一百八十三篇:之Java高级Swing编程之JEditorPane组件与进度指示器在Java编程的学习旅程中,我们始终保持着积极探索、共同成长的态度。今天,我们将深入学习Java高级Swing编程中关于JEditorPane组件与进度指示器的部分,包括JEditorPane组件的功能特性......
  • Java学习,删除集合指定元素
    Java删除集合中指定元素,通常依赖于集合具体类型。不同的集合类型(如ArrayList,HashSet,LinkedList等)提供了不同的方法来执行此操作。使用ArrayList:importjava.util.ArrayList;importjava.util.List; publicclassMain{  publicstaticvoidmain(String[]ar......
  • 小志的Java学习计划
    小志的Java学习计划自身情况分析及目标​普通二本计算机软件工程专业,大学期间未参加比赛,绩点和个人技术水平也不高只能说可以保证毕业。一战考研数学发挥失利。受到网络上学历贬值的信息的影响,考虑到本身报考院校也不是出色的双非院校三年以后就业也许也不容易,于是并不打......
  • [2025.1.19 JavaSE学习]网络编程-2(netstat指令 && TCP补充)
    netstatnetstat-an:可以查看当前主机网络情况,包括端口监听情况和网络连接情况netstat-an|more:可以分页显示在dos控制台执行Listening表示某个端口在监听如果有一个外部程序(客户端)连接到该端口,就会显示一条连接信息PS:netstat-anb,可以发现,8888端口号在上一节程序运行......
  • 【开源】一款基于JAVA的国产化自主可控的人工智能开源平台
    一、项目简介人工智能开源平台是由联合国内顶尖科研力量共同打造的国产化自主可控的人工智能开源平台。平台面向人工智能研究中的数据处理、算法开发、模型训练、算力管理和推理应用等各个流程的技术难点,研发了包括一站式算法开发平台、高性能分布式深度学习框架、先进算法模型库......
  • 【华为OD-E卷 - 最长连续子序列 100分(python、java、c++、js、c)】
    【华为OD-E卷-最长连续子序列100分(python、java、c++、js、c)】题目有N个正整数组成的一个序列。给定整数sum,求长度最长的连续子序列,使他们的和等于sum,返回此子序列的长度,如果没有满足要求的序列,返回-1输入描述第一行输入是:N个正整数组成的一个序列第二行输入是:给定......
  • 【华为OD-E卷 - 找出两个整数数组中同时出现的整数 100分(python、java、c++、js、c)】
    【华为OD-E卷-找出两个整数数组中同时出现的整数100分(python、java、c++、js、c)】题目现有两个整数数组,需要你找出两个数组中同时出现的整数,并按照如下要求输出:有同时出现的整数时,先按照同时出现次数(整数在两个数组中都出现并目出现次数较少的那个)进行归类,然后按照出......