JDK基础类库java.util包下那些你可能不知道的工具们大盘点(一)EnumMap、EnumSet【享学Java】(上)

简介: JDK基础类库java.util包下那些你可能不知道的工具们大盘点(一)EnumMap、EnumSet【享学Java】(上)

前言


聊了很长时间逻辑性非常强的Spring框架,这篇文章来些轻松点的内容:聊聊JDK基础类库中那些你可能不知道的工具。


正所谓玩好JDK,面试不用愁。那么JDK好掌握吗,答案是非常难。

大家平时理解的JDK可能就只是指的它的基础类库部分,但其实此处可扫盲一下。JDK主要包含有如下三个部分:


  1. Java运行时环境(JRE),说白了就是JVM
  2. Java的基础类库(它数量可观,功能强大,覆盖面广。对开发者来说是最为中重要的部分)
  3. Java的开发工具(比如javac、jmap、jconsole、jstack、jvisualvm等等)


本文将着眼于第二部分:基础类库,并且讲解的也只还是它的冰山一角,因为本文主要还是着眼于我认为的(本人水平有限)还能稍微比较常用的一些工具。

工具们介绍


下面介绍的工具,大都来自于java.util包。其实之前也有好几篇相关的工具类介绍文章。

为何专门篇幅介绍工具?因为我认为:工欲善其事必先利其器。你掌握的工具越多,你才能做好更多的事。

【小家java】聊聊Java中的java.util.Arrays类和java.util.Collections工具类

【小家Java】Java第二API之apache的commons-lang3工具包史上最完整的讲解(书写优雅代码必备工具)

【小家java】Java中Apache Commons-lang3提供的DateUtils等时间、日期工具类


EnumMap和EnumSet


这两个哥们都是JDK1.5提供的。


EnumMap


EnumMap它也属于Map体系的东西,该类是专门针对枚举类设计的一个Map集合类。集合中的所有键必须是同一个枚举类的实例,它的key为枚举元素,value自定义。


其实有小伙伴包括我也这样疑问过,我们都可以自己使用Map来实现,为何要使用EnumMap呢?

答案是:它的性能高。因为它的内部是**用数组的数据结构**来维护的!


使用Demo:

public enum Color {
    RED, BLACK, WHITE, GREEN
}
public class Main {
    public static void main(String[] args) {
        // 它没有空构造,必须制定枚举class类型。当然构造函数也可以接收一个Mapnew EnumMap<>(EnumMap/Map);
        Map<Color, String> map = new EnumMap<>(Color.class);
        map.put(Color.RED, "红色");
        map.put(Color.BLACK, "黑色");
        map.put(Color.WHITE, "白色");
        map.put(Color.WHITE, "白色"); // 故意重复放一个
        // 遍历:发现EnumMap它的存储是有序的
        map.forEach((k, v) -> System.out.print(k + ":" + v + "   "));
        System.out.println();
        // 可以看到,它并不会发生并发修改异常ConcurrentModificationException
        map.forEach((k, v) -> {
            if (k == Color.WHITE) {
                map.put(Color.GREEN, "绿色");
            }
            System.out.print(k + ":" + v + "   ");
        });
        // key不允许为null  java.lang.NullPointerException
        map.put(null,"未知");
    }
}


运行结果如下:

RED:红色   BLACK:黑色   WHITE:白色   
RED:红色   BLACK:黑色   WHITE:白色   GREEN:绿色   
红色
Exception in thread "main" java.lang.NullPointerException
  at java.util.EnumMap.typeCheck(EnumMap.java:743)
  at java.util.EnumMap.put(EnumMap.java:267)
  at java.util.EnumMap.put(EnumMap.java:79)
  at com.fsx.maintest.Main.main(Main.java:40)


从上示例对EnumMap总结出以下几点:


  1. EnumMap是有序的,这个顺序是按照你枚举类的定义顺序走的
  2. EnumMap可以一边遍历一边修改,不会抛ConcurrentModificationException异常
  3. EnumMap的key不允许为null
  4. EnumMap是线程不安全的(若需要安全可以使用Collections#synchronizedMap来一下子)
  5. EnumMap效率高,所有操作都是常量时间。(因为底层是数组)


从源码处稍微了解一下为何它的效率高?


// @since 1.5  继承自AbstractMap,所以它也能够当作Map来使用
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements java.io.Serializable, Cloneable {
  // 所有key都必须这个类型
    private final Class<K> keyType; 
    // 简单的说它把所有的key(枚举)都缓存起来  此处用的数组
    private transient K[] keyUniverse;
  // 所有的值  也是数组  它的length和keyUniverse是相同的
    private transient Object[] vals;
  // 它的get方法,直接根据ordinal()去数组找了  这就是它快最为核心的原因
    public V get(Object key) {
        return (isValidKey(key) ? unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
    }
}


它的get方法,直接使用的ordinal()去数组里拿值,那可不效率高吗。


最后介绍:EnumMap还有一种用法是被继承:


public class ColorMap extends EnumMap<Color, String> {
    public ColorMap() {
        super(Color.class);
    }
}
public class Main {
    public static void main(String[] args) {
        Map<Color, String> map = new ColorMap();
        map.put(Color.BLACK, "黑色");
        System.out.println(map.get(Color.BLACK)); //黑色
    }
}

EnumSet


EnumSet是一个专为枚举设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式地指定。

同样的我们使用它的原因是:效率高(EnumSetHashSet更快)。


使用Demo:


public class Main {
    public static void main(String[] args) {
        Set<Color> set = EnumSet.allOf(Color.class);
        System.out.println(set); //[RED, BLACK, WHITE, GREEN]
        set = EnumSet.noneOf(Color.class);
        System.out.println(set); //[]
        // 添加元素
        set.add(Color.RED);
        set.add(Color.BLACK);
        System.out.println(set); //[RED, BLACK]
        // 自己人工指定
        set = EnumSet.of(Color.WHITE, Color.BLACK, Color.WHITE);
        System.out.println(set); //[BLACK, WHITE]  请注意这个顺序并不是上面add的顺序  毕竟自己去重了
        // 根据区间来创建
        set = EnumSet.range(Color.BLACK, Color.GREEN);
        //set = EnumSet.range(Color.GREEN, Color.BLACK); // 这样抛出异常了, java.lang.IllegalArgumentException: GREEN > BLACK
        System.out.println(set); //[BLACK, WHITE, GREEN] 可以看到含头含尾的
        // 相当于取差集的意思~
        System.out.println(EnumSet.complementOf((EnumSet) set)); //[RED]
        // copyOf方法克隆一个(请注意是克隆,并不是返回的一个视图)
        System.out.println(EnumSet.copyOf((EnumSet) set)); //[BLACK, WHITE, GREEN]
    }
}


输出结果:


[RED, BLACK, WHITE, GREEN]
[]
[RED, BLACK]
[BLACK, WHITE]
[BLACK, WHITE, GREEN]
[RED]
[BLACK, WHITE, GREEN]


EnumSet是个抽象类,所以构建它只能通过static方法。它通过内建的实现类RegularEnumSet来保证高效率,下面可以简单看看它为何这么优秀的原因。

RegularEnumSet #add()方法源码如下:


// @since 1.5
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable, java.io.Serializable { ... }
//@since 1.5  访问权限非public 属于内建类
class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
    public boolean add(E e) {
        typeCheck(e);
        long oldElements = elements;
        elements |= (1L << ((Enum<?>)e).ordinal());
        return elements != oldElements;
    }
}


这个实现非常非常非常的高大上:add方法只是对长整型数据element做了一个操作而已,也就是说EnumSet实际上将枚举值ordinal()保存在一个长整型long数据上,每个枚举值占用一bit。


我们知道Long型是64bit,那枚举值数量超过64咋办呢? 其实这个时候JDK就会用EnumSet的另一个实现JumboEnumSet

实际情况是:试问一下,谁的枚举值能定义64个这么多???拉出去斩了???


另还注意这里指的是64bit,也就是说是8byte。(计算机中一个字节(byte)占8位(bit))


总而言之,EnumSet的实现是非常高级,效率也是非常的高的。

StringTokenizer和StringJoiner


StringTokenizer


Java中的StringTokenizer类用于将字符串分解为标记,效果类似split,但split方法@since 1.4,而StringTokenizer 1.0版本就有了。


使用Demo:


public class Main {
    public static void main(String[] args) {
    // 它只能靠构造函数把分隔符传进去
    // 若不指定分隔符,默认使用" \t\n\r\f"去分割
        StringTokenizer st1 = new StringTokenizer("Hello Geeks How are you", " ");
        while (st1.hasMoreTokens())
            System.out.println(st1.nextToken());
        StringTokenizer st2 = new StringTokenizer("JAVA : Code : String", " :");
        while (st2.hasMoreTokens())
            System.out.println(st2.nextToken());
        StringTokenizer st3 = new StringTokenizer("JAVA : Code : String", " :", true);
        while (st3.hasMoreTokens())
            System.out.println(st3.nextToken());
    }
}


StringJoiner:拯救字符串拼接


它是JDK8新增的一个工具类,位于java.util包下。那么为何JDK8要新增这样一个类呢?原因是之前的StringBuilder太死板了,不支持分隔


比如我们经常会有这样的需求:把一个字符串数组用,分隔开,但是最后一个不要逗号?


public class Main {
    public static void main(String[] args) {
        String[] strs = {"wo", "ai", "ni"};
        // 使用原始的StringBuider方式~
        StringBuilder sb = new StringBuilder();
        Arrays.stream(strs).forEach(s -> {
            sb.append(s);
            sb.append(",");
        });
        System.out.println(sb.substring(0, sb.lastIndexOf(","))); //wo,ai,ni
    // String内置的工具类方式  它底层还是使用的StringBuilder
        System.out.println(StringUtils.arrayToDelimitedString(strs, ",")); //wo,ai,ni
        //使用StringJoiner
        StringJoiner sj = new StringJoiner(",");
        Arrays.stream(strs).forEach(x -> sj.add(x));
        System.out.println(sj); //wo,ai,ni
    }
}


StringJoiner它主要就是掌握构造函数+add这个方法,其余方法不常用就不介绍了~


虽然它最终实现还是依赖StringBuilder,但是它的实现还是很高效的,特别是有一些小技巧比如对前缀、后缀的处理~


相关文章
|
11月前
|
人工智能 缓存 监控
使用LangChain4j构建Java AI智能体:让大模型学会使用工具
AI智能体是大模型技术的重要演进方向,它使模型能够主动使用工具、与环境交互,以完成复杂任务。本文详细介绍如何在Java应用中,借助LangChain4j框架构建一个具备工具使用能力的AI智能体。我们将创建一个能够进行数学计算和实时信息查询的智能体,涵盖工具定义、智能体组装、记忆管理以及Spring Boot集成等关键步骤,并展示如何通过简单的对话界面与智能体交互。
3636 1
|
监控 Java Unix
6个Java 工具,轻松分析定位 JVM 问题 !
本文介绍了如何使用 JDK 自带工具查看和分析 JVM 的运行情况。通过编写一段测试代码(启动 10 个死循环线程,分配大量内存),结合常用工具如 `jps`、`jinfo`、`jstat`、`jstack`、`jvisualvm` 和 `jcmd` 等,详细展示了 JVM 参数配置、内存使用、线程状态及 GC 情况的监控方法。同时指出了一些常见问题,例如参数设置错误导致的内存异常,并通过实例说明了如何排查和解决。最后附上了官方文档链接,方便进一步学习。
3338 4
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
468 0
|
11月前
|
人工智能 Java API
Java AI智能体实战:使用LangChain4j构建能使用工具的AI助手
随着AI技术的发展,AI智能体(Agent)能够通过使用工具来执行复杂任务,从而大幅扩展其能力边界。本文介绍如何在Java中使用LangChain4j框架构建一个能够使用外部工具的AI智能体。我们将通过一个具体示例——一个能获取天气信息和执行数学计算的AI助手,详细讲解如何定义工具、创建智能体并处理执行流程。本文包含完整的代码示例和架构说明,帮助Java开发者快速上手AI智能体的开发。
3580 8
|
10月前
|
人工智能 监控 Java
Java与AI智能体:构建自主决策与工具调用的智能系统
随着AI智能体技术的快速发展,构建能够自主理解任务、制定计划并执行复杂操作的智能系统已成为新的技术前沿。本文深入探讨如何在Java生态中构建具备工具调用、记忆管理和自主决策能力的AI智能体系统。我们将完整展示从智能体架构设计、工具生态系统、记忆机制到多智能体协作的全流程,为Java开发者提供构建下一代自主智能系统的完整技术方案。
1148 4
|
Java 数据安全/隐私保护 计算机视觉
银行转账虚拟生成器app,银行卡转账截图制作软件,java实现截图生成工具【仅供装逼娱乐用途】
本内容提供Java生成自定义图片的示例代码,涵盖基础图像创建、文本添加及保存功能,适合学习2D图形编程。包括教学示例图片生成、文本图层处理和数字水印技术实现方案。
|
人工智能 自然语言处理 Java
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
FastExcel 是一款基于 Java 的高性能 Excel 处理工具,专注于优化大规模数据处理,提供简洁易用的 API 和流式操作能力,支持从 EasyExcel 无缝迁移。
4071 65
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
|
安全 Java 编译器
JD-GUI,java反编译工具及原理: JavaDecompiler一个Java反编译器
Java Decompiler (JD-GUI) 是一款由 Pavel Kouznetsov 开发的图形化 Java 反编译工具,支持 Windows、Linux 和 Mac Os。它能将 `.class` 文件反编译为 Java 源代码,支持多文件标签浏览、高亮显示,并兼容 Java 5 及以上版本。JD-GUI 支持对整个 Jar 文件进行反编译,可跳转源码,适用于多种 JDK 和编译器。其原理基于将字节码转换为抽象语法树 (AST),再通过反编译生成代码。尽管程序可能带来安全风险,但可通过代码混淆降低可读性。最新版修复了多项识别错误并优化了内存管理。
11038 1
|
Java 数据安全/隐私保护
银行转账虚拟生成器app,银行卡转账截图制作软件,java实现截图生成工具【仅供装逼娱乐用途】
本项目提供了一套基于Java的图片处理教学方案,包含自定义图片生成、图像水印添加及合法电子凭证生成技术示例。
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
1197 7
Spring Boot 入门:简化 Java Web 开发的强大工具