Map&泛型
发布日期:2021-05-14 16:55:59 浏览次数:14 分类:精选文章

本文共 3350 字,大约阅读时间需要 11 分钟。

Map 与泛型 - Java 开发指南

Map是一个高效存储键值对的数据结构,在应用开发中扮演着重要角色。本文将详细介绍Map的实现方式、常见实现类以及使用场景,并深入探讨Java中的泛型概念。

Map 的本质

Map是一种数据结构,用于存储键值对。每个键只能对应一个唯一的值,但值可以是多个键映射到它。Map的特点是键的存取是无序的,除非是LinkedHashMap这种有序的实现。此外,Map不允许键重复,但允许值重复。

HashMap 的特点

HashMap是最常用的Map实现类,基于哈希表实现,具有以下特点:

  • 线程不安全:适合单线程环境。
  • 可以存储null键:但null键不能作为Map的值。
  • 扩容方式:当存储元素时,如果哈希溢出或超过负载因子,会扩容,扩容时会将Map的长度翻倍。

![(Map的哈希存储方式)] Map中的键通过哈希值存储,存取时快速定位,若链表长度超过8时会优化为红黑树。

Hashtable 的特点

Hashtable与HashMap类似,但具有线程安全性。使用Hashtable时,所有操作都需进行同步。另外,Hashtable不允许键或值为null,这在处理空值时需要特别注意。

  • 线程安全:适合多线程环境的数据共享需求。
  • 不允许null键和值:在使用时需注意这一限制。

TreeMap 的特点

TreeMap的特点是基于红黑树实现,键的存储和取出有序。 TreeMap不仅支持自然排序,还支持定制排序、逆序排序以及树形结构,可根据需求进行扩展。

  • 自然排序或定制排序:默认为自然排序,支持定制键的比较逻辑。
  • TreeEntry结构:Store为节点,包含键和值,树形结构决定了查找效率。

ConcurrentHashMap 的特点

ConCurrentHashMap是针对多线程环境优化的Map实现类,它采用多个哈希链表的方式实现锁粗化,解除简单偏向性的锁并发效率瓶颈。

  • 高效多线程:通过细粒化锁控制并发访问,提升性能。
  • 扩容过程:在扩容时不会阻止映射操作,提升性能。

LinkedHashMap 的特点

LinkedHashMap与HashMap功能相似,但有以下特点:

  • 有序性:按照插入顺序存取数据。
  • 双向链表:实现有序存取,支持逆序遍历。

遍历方式

Map的遍历主要有以下三种方式:

  • entrySet():返回所有键值对集合, 适用于需要同时处理键和值的场景。
  • keySet():返回所有键的集合, 适用于只需要操作键的场景。
  • values():返回所有值的集合, 适用于只需要操作值的场景。
  • 以下示例展示如何使用 ConcurrentHashMap 的entrySet()遍历键值对:

    ConcurrentHashMap
    map = new ConcurrentHashMap<>();
    map.put("age", 25);
    map.put("height", 180);
    Set
    > entrySet = map.entrySet();
    for (Map.Entry
    entry : entrySet) {
    System.out.println("key: " + entry.getKey());
    System.out.println("value: " + entry.getValue());
    }

    Properties 的应用

    Properties是一个特殊的Hashtable,用于存储键值对的应用配置文件,主要用于减少硬编码依赖,提高程序的灵活性。

    Properties 的主要特点

    • 键值对类型:键和值都为String类型,限定了 Properties 使用范围。
    • 配置文件格式:支持 .properties、 .xml、 .ini、 .yaml 等格式的配置文件读取和解析。
    • 易于维护:通过 separated by newline 的格式,支持Unicode编码的配置文件完整性维护。

    使用 Properties 读取配置文件

    想要将配置文件中的数据读取到程序中,可以按照以下步骤进行:

  • 首先导入必要的 Properties 类。
  • 使用 FileInputStream 读取配置文件。
  • 调用 load() 方法将配置文件内容加载到 Properties 对象中。
  • 调用 getProperty() 方法获取相应的配置值。
  • 以下是一个典型的读取和保存配置文件示例:

    import java.util.Properties;
    public class PropertiesTest {
    public static void main(String[] args) {
    Properties properties = new Properties();
    // 设置 properties
    properties.setProperty("username", "root");
    properties.setProperty("password", "123456");
    // 保存到文件
    properties.store(new FileOutputStream("application.properties"), "保存的注释");
    System.out.println(properties.storeID); // 输出存储的ID
    }
    }

    配置文件类型判断

    配置文件的文件扩展名决定了其类型:

    • .properties:使用 Properties 类读取和解析。
    • .xml:可选使用 XML 解析器读取。
    • .ini:采用 ini4j 或相应库解析。
    • .yaml: 使用 snakeyaml 库解析。

    泛型的应用

    泛型是一种强大的编程概念,可通过指定类型增强程序的可读性和安全性。本节将探讨泛型的作用、使用方法以及注意事项。

    泛型的作用

    • 类型校验:开发者可以在编译阶段确保类型的正确性。
    • 代码简洁性:通过泛型,可以避免强制类型转换,提升代码可读性。
    • 代码扩展性:通过泛型接口,程序能够支持多种数据类型增强灵活性。

    泛型的使用场景

    • 泛型类:在类定义时指定一个或多个类型参数,例如 List
      或 Map<K,V>。
    • 泛型方法:在方法签名中定义类型参数,提升方法的通用性。

    泛型的上限和下限

    通过设置泛型的上限和下限,可进一步增强类型的安全性。例如:

    • 返回类型:使用 extends 表示返回值的上限,可以通过扩展关系限定类型。例如:

      public 
      T myMethod() { ... }
    • 参数类型:使用 super 表示类型的下限,可强制指定类型范围。例如:

      public 
      void myMethod(T t) { ... }

    泛型嵌套

    泛型的嵌套使得复杂数据结构的设计成为可能。例如,以下 Set 中的元素是 Map:

    Set
    > entrySet = ...;

    泛型注意事项

    • 基本数据类型不能使用泛型:如 List 是正确的,但 List
      数组中不能存放基本数据类型。
    • 泛型中的继承关系:在定义泛型时,如果有类继承关系,应谨慎处理类型隐式转换。
    • class A {}
      class B extends A {}
      List listB = new ArrayList<>();
      List listA = new ArrayList<>();
      listA = listB; // 错误

      总结

      Map是Java中重要的数据结构,其实现方式和应用场景多样。从 HashMap 到 ConcurrentHashMap,各种实现类各有特点,适用于不同的场景。同时,泛型作为Java的一个核心概念,提升了程序的灵活性和可扩展性。理解和掌握 Map 和 泛型 的使用,能够显著提升Java开发能力。

    上一篇:File类&递归
    下一篇:List&Set集合

    发表评论

    最新留言

    很好
    [***.229.124.182]2025年04月19日 02时50分25秒