编程自学指南:java程序设计开发,Java Map 集合,Map 集合的概念、特点和用途,HashMap、TreeMap 和 LinkedHashMap 的使用方法和区别,Map 集合的内部实现原理

编程自学指南:java程序设计开发,Java Map 集合详解课件

一、课程信息

学习目标

  1. 理解 Map 集合的概念、特点和用途。
  2. 掌握 HashMap、TreeMap 和 LinkedHashMap 的使用方法和区别。
  3. 学会使用 Map 集合解决实际问题,如数据存储和查找。
  4. 了解 Map 集合的内部实现原理。

课程重点

  1. Map 集合的特点和常用方法。
  2. HashMap、TreeMap 和 LinkedHashMap 的使用场景。
  3. 键值对的存储和访问机制。

课程难点

  1. HashMap 的哈希冲突处理机制。
  2. TreeMap 的排序规则和自定义排序。

二、课程导入

生活中的映射关系

在生活中,我们经常会遇到各种映射关系。例如,学生的学号和姓名之间存在一种映射关系,一个学号对应一个学生的姓名;电话号码和联系人姓名之间也存在映射关系,一个电话号码对应一个联系人。在图书馆中,图书的编号和图书的信息也构成了映射关系。

编程中的映射需求

在 Java 编程中,我们也会经常遇到需要存储和处理映射关系的场景。例如,统计一篇文章中每个单词出现的次数,需要将单词作为键,出现的次数作为值进行存储;存储学生的成绩信息,需要将学生的姓名作为键,成绩作为值进行存储。使用普通的数组或列表来实现这些映射关系比较麻烦,而 Java 提供的 Map 集合可以很好地解决这个问题。

示例代码引入

import java.util.ArrayList;
import java.util.List;

// 模拟使用普通列表存储学生成绩信息
class StudentScore {
    String name;
    int score;

    public StudentScore(String name, int score) {
        this.name = name;
        this.score = score;
    }
}

public class ScoreStorageExample {
    public static void main(String[] args) {
        List<StudentScore> scores = new ArrayList<>();
        scores.add(new StudentScore("Alice", 85));
        scores.add(new StudentScore("Bob", 90));

        // 查找 Alice 的成绩比较麻烦
        for (StudentScore score : scores) {
            if (score.name.equals("Alice")) {
                System.out.println("Alice's score: " + score.score);
                break;
            }
        }
    }
}

使用 Map 集合可以更简洁地实现:

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

public class MapScoreStorageExample {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Alice", 85);
        scores.put("Bob", 90);

        // 查找 Alice 的成绩很方便
        Integer aliceScore = scores.get("Alice");
        System.out.println("Alice's score: " + aliceScore);
    }
}

三、Map 集合概述

定义

Map 是 Java 集合框架中的一个接口,它用于存储键值对(key - value)映射关系。每个键在 Map 中是唯一的,一个键对应一个值。

特点

  1. 键的唯一性:Map 中的键是唯一的,不能有重复的键。如果尝试使用相同的键存储不同的值,后面的值会覆盖前面的值。
  2. 无序性(部分实现):HashMap 和 LinkedHashMap 不保证键值对的插入顺序,而 TreeMap 会根据键的自然顺序或自定义顺序对键值对进行排序。
  3. 键和值可以为 null:在 HashMap 和 LinkedHashMap 中,键和值都可以为 null,但在 TreeMap 中,键不能为 null。

常用实现类

  1. HashMap:基于哈希表实现,不保证键值对的顺序,元素的存储位置由键的哈希值决定。
  2. TreeMap:基于红黑树实现,会根据键的自然顺序或自定义顺序对键值对进行排序。
  3. LinkedHashMap:基于哈希表和链表实现,既保证了键值对的唯一性,又能保持键值对的插入顺序。

四、HashMap 详解

内部实现原理

HashMap 内部使用哈希表来存储键值对。哈希表是由数组和链表(或红黑树,当链表长度超过一定阈值时)组成的。当向 HashMap 中插入一个键值对时,首先会计算键的哈希值,然后根据哈希值找到对应的桶(数组的一个位置),如果桶中没有元素,则直接将键值对放入桶中;如果桶中已经有元素,则会遍历链表(或红黑树),比较键是否相等,如果相等则更新值,否则将新的键值对插入到链表(或红黑树)中。

示例代码

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

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

        // 添加键值对
        ages.put("Alice", 20);
        ages.put("Bob", 25);
        ages.put("Charlie", 30);

        // 获取值
        Integer aliceAge = ages.get("Alice");
        System.out.println("Alice's age: " + aliceAge);

        // 判断键是否存在
        boolean containsBob = ages.containsKey("Bob");
        System.out.println("Contains Bob: " + containsBob);

        // 删除键值对
        ages.remove("Charlie");
        System.out.println("After removing Charlie: " + ages);

        // 遍历键值对
        for (Map.Entry<String, Integer> entry : ages.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}

哈希冲突处理

当不同的键计算出相同的哈希值时,就会发生哈希冲突。HashMap 使用链地址法来处理哈希冲突,即每个桶中存储一个链表(或红黑树),当发生哈希冲突时,将冲突的键值对添加到链表(或红黑树)中。

键的唯一性判断

HashMap 判断键是否相等的规则是:首先比较键的哈希值,如果哈希值不同,则认为键不相等;如果哈希值相同,则调用键的 equals() 方法进一步比较。因此,为了保证键的唯一性,重写 equals() 方法时必须同时重写 hashCode() 方法。

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

class Person {
    private String name;
    private int age;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && name.equals(person.name);
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

public class HashMapCustomKeyExample {
    public static void main(String[] args) {
        Map<Person, String> personAddresses = new HashMap<>();
        Person alice = new Person("Alice", 20);
        personAddresses.put(alice, "123 Main St");
        personAddresses.put(new Person("Alice", 20), "456 Elm St"); // 重复键,值会覆盖

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

五、TreeMap 详解

内部实现原理

TreeMap 内部使用红黑树(一种自平衡的二叉搜索树)来存储键值对。红黑树可以保证键值对按照键的顺序进行排序,并且插入、删除和查找操作的时间复杂度都是 O (log n)。

示例代码

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

public class TreeMapExample {
    public static void main(String[] args) {
        // 创建 TreeMap 对象
        Map<Integer, String> numbers = new TreeMap<>();

        // 添加键值对
        numbers.put(3, "Three");
        numbers.put(1, "One");
        numbers.put(2, "Two");

        // 遍历键值对
        for (Map.Entry<Integer, String> entry : numbers.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue()); // 输出按键排序的结果
        }

        // 获取第一个和最后一个键值对
        Map.Entry<Integer, String> firstEntry = ((TreeMap<Integer, String>) numbers).firstEntry();
        Map.Entry<Integer, String> lastEntry = ((TreeMap<Integer, String>) numbers).lastEntry();
        System.out.println("First: " + firstEntry);
        System.out.println("Last: " + lastEntry);
    }
}

自然排序和自定义排序

  • 自然排序:如果键实现了 Comparable 接口,TreeMap 会按照键的自然顺序进行排序。例如,IntegerString 等类都实现了 Comparable 接口。
import java.util.TreeMap;
import java.util.Map;

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

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

    @Override
    public int compareTo(Student other) {
        return Integer.compare(this.score, other.score);
    }

    @Override
    public String toString() {
        return "Student{name='" + name + "', score=" + score + "}";
    }
}

public class TreeMapNaturalOrderExample {
    public static void main(String[] args) {
        Map<Student, String> studentGrades = new TreeMap<>();
        studentGrades.put(new Student("Alice", 80), "B");
        studentGrades.put(new Student("Bob", 90), "A");
        studentGrades.put(new Student("Charlie", 70), "C");

        for (Map.Entry<Student, String> entry : studentGrades.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}
  • 自定义排序:如果键没有实现 Comparable 接口,或者需要按照自定义的规则进行排序,可以在创建 TreeMap 时传入一个 Comparator 对象。
import java.util.Comparator;
import java.util.TreeMap;
import java.util.Map;

class Book {
    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    public String getTitle() {
        return title;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "Book{title='" + title + "', price=" + price + "}";
    }
}

public class TreeMapCustomOrderExample {
    public static void main(String[] args) {
        // 自定义比较器,按价格排序
        Comparator<Book> priceComparator = Comparator.comparingDouble(Book::getPrice);

        Map<Book, String> bookCategories = new TreeMap<>(priceComparator);
        bookCategories.put(new Book("Java Programming", 50.0), "Programming");
        bookCategories.put(new Book("Python Basics", 30.0), "Programming");
        bookCategories.put(new Book("Data Structures", 70.0), "Computer Science");

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

六、LinkedHashMap 详解

内部实现原理

LinkedHashMap 是 HashMap 的子类,它在 HashMap 的基础上维护了一个双向链表,用于记录键值对的插入顺序。因此,LinkedHashMap 既保证了键值对的唯一性,又能保持键值对的插入顺序。

示例代码

import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapExample {
    public static void main(String[] args) {
        // 创建 LinkedHashMap 对象
        Map<String, Integer> cities = new LinkedHashMap<>();

        // 添加键值对
        cities.put("Beijing", 2000);
        cities.put("Shanghai", 1800);
        cities.put("Guangzhou", 1500);

        // 遍历键值对
        for (Map.Entry<String, Integer> entry : cities.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue()); // 按插入顺序输出
        }
    }
}

七、HashMap、TreeMap 和 LinkedHashMap 的比较

比较项HashMapTreeMapLinkedHashMap
内部实现哈希表红黑树哈希表 + 双向链表
键值对顺序无序有序(自然排序或自定义排序)保持插入顺序
插入、删除、查找效率O(1)O(log n)O(1)
适用场景不需要键值对顺序,注重插入、删除和查找效率需要键值对按键排序需要保持键值对插入顺序

八、课堂练习

练习 1

编写一个程序,使用 HashMap 统计一个字符串中每个字符出现的次数,并输出结果。

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

public class Exercise1 {
    public static void main(String[] args) {
        String str = "hello world";
        // 请完成代码
    }
}

练习 2

编写一个程序,使用 TreeMap 对一组学生对象(包含学生姓名和成绩)按照成绩从高到低进行排序,并输出排序后的结果。可以通过自定义比较器来实现。

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

class Student {
    String name;
    int score;

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

    @Override
    public String toString() {
        return "Student{name='" + name + "', score=" + score + "}";
    }
}

public class Exercise2 {
    public static void main(String[] args) {
        // 请完成代码
    }
}

练习 3

编写一个程序,使用 LinkedHashMap 存储一组商品对象(包含商品名称和价格),并保持商品的插入顺序,最后输出这些商品的信息。

import java.util.LinkedHashMap;
import java.util.Map;

class Product {
    String name;
    double price;

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Product{name='" + name + "', price=" + price + "}";
    }
}

public class Exercise3 {
    public static void main(String[] args) {
        // 请完成代码
    }
}

九、课程总结

重点回顾

  1. Map 集合的概念、特点和常用实现类。
  2. HashMap、TreeMap 和 LinkedHashMap 的内部实现原理、使用方法和区别。
  3. 键值对的存储和访问机制,以及键的唯一性判断。

注意事项

  1. 在使用 HashMap 时,要确保键的 equals() 方法和 hashCode() 方法正确重写。
  2. 在使用 TreeMap 时,键必须实现 Comparable 接口或传入自定义的 Comparator 对象,且键不能为 null。

十、课后作业

作业 1

编写一个程序,使用 HashMap 存储一组员工对象(包含员工编号、姓名和工资),并根据员工编号查找员工信息。要求重写员工类的 equals() 方法和 hashCode() 方法。

作业 2

编写一个程序,使用 TreeMap 对一组图书对象(包含图书编号、书名和价格)按照价格从低到高进行排序,并输出排序后的结果。可以通过自定义比较器来实现。

作业 3

编写一个程序,使用 LinkedHashMap 存储一组客户对象(包含客户编号、姓名和联系方式),并保持客户的插入顺序,最后输出这些客户的信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zl515035644

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

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

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

打赏作者

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

抵扣说明:

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

余额充值