Hutool 工具不糊涂

10,270 阅读13分钟

前言

这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战 。还在对项目中的工具类和工具方法进行封装吗?让 Hutool 帮你,它是项目中 util 包的友好替代,覆盖了 Java 开发底层的方方面面,既是大型项目中解决小问题的利器,也是小项目中的效率担当,它能让你专注业务,极大地提升开发效率,可以最大程度地避免自行封装出现的小问题、小 Bug。所以,学会如何高效、准确地使用项目开发过程中所需的工具方法非常有必要,今天结合使用场景给大家分享一些实用的工具类方法,没讲清楚以及未涉及之处,望大家见谅,具体详情可参见 Hutool 官网 (hutool.cn)

引入

Hutool 包含对文件、流、加密解密、转码、正则、线程、XML 等 JDK 方法进行封装,放在不同的模块组件当中,当你需要对 Excel 进行操作时,你可以单独引入 hutool-poi 模块,当然分不清个模块之间功能,图省事,也可以通过引入 hutool-all 方式引入所有模块。

Maven

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.16</version>
</dependency>

Gradle

implementation 'cn.hutool:hutool-all:5.7.16'

jar

下载传送门

使用

下面给大伙分享一些经常使用且使用的工具方法,有用到过的欢迎留言评论哦 (^▽^)

Console 对象信息打印类

熟悉 JS 的掘友看到这两个方法一定不陌生,甚至是“旧交”了,其实 Hutool 中 的 Console 对象借鉴的就是 JS 中的语法糖。虽说是打印,和 System.out.println() / System.err.println() 还是有所不同的,最重要的是它支持 Slf4j 的字符串模版语法,会自动将对象(包括数组)转为字符串形式。

代码实例:


Console.log("this is array: {}", new String[]{"Java", "JavaScript", "Python"});

Console.error("I'm a red error message.");

这里的 {} 作为模版占位符,能够将逗号右边的变量值依次传入,从而以字符串的形式打印输出。

Convert 不同类型之间转换类

该类中封装了针对 Java 常见类型的转换,用于简化类型转换,同时对转换失败的异常捕获有着很好的封装,能够帮你减轻业务代码的臃肿,提升代码的优雅性,该工具类还是非常常用和实用的,必须安利一波୧(๑•̀◡•́๑)૭

代码实例:


// 转换为字符串
int number = 10;
Console.log("转换成字符串后:{}", Convert.toStr(number));

int[] arr = {1, 2, 3, 4, 5};
Console.log(Convert.toStr(arr, "Convert to string fail! This is default value."));

// 转换为指定类型的数组
String[] letters = {"12", "122", "1223", "1211"};
Console.log(Convert.toShortArray(letters));
Console.log(Convert.toStrArray(arr));

float[] floatArr = {1.2F, 1.3F, 2.0F, 3.14f};
Integer[] integerArr = Convert.toIntArray(floatArr);
Console.log(integerArr); // [1, 1, 2, 3]

// 转换为日期对象
String strDate1 = "2021-10-11 12:03:29";
// toDate
Console.log("The string of data convert to Date: {}", Convert.toDate(strDate1)); // 2021-10-11 12:03:29
// toLocalDateTime
Console.log("The string of data convert to LocalDateTime: {}", Convert.toLocalDateTime(strDate1)); // 2021-10-11T12:03:29

// 转换成集合
String[] langs = {"Java", "JavaScript", "Python", "C++", "GoLang", "TypeScript", "Kotlin"};

// 通过 Convert.convert(Class<T>, Object) 方法可以将任意类型转换为指定类型
ArrayList arrayList = Convert.convert(ArrayList.class, langs);
// 也可以这样转集合
List<String> langList = Convert.toList(String.class, langs);
Console.log("String array is converted to ArrayList: {}", arrayList);
Console.log("String array is converted to List: {}", langList);
// 转成指定类型的集合
Object[] objArr = {"a", "你", "好", "", 1};
// TypeReference 对象可以对嵌套泛型进行类型转换
List<String> strList = Convert.convert(new TypeReference<List<String>>() {
}, objArr);
Console.log("使用 TypeReference 对象可以对嵌套泛型进行类型转换:{}", strList);

字符串转为 16 进制(Hex)和 Unicode


// 字符串转为 16 进制(Hex)
String desc = "大家好,我的名字叫 HUALEI !";
// 因为字符串牵涉到编码问题,因此必须传入编码对象
String hexStr = Convert.toHex(desc, CharsetUtil.CHARSET_UTF_8);
Console.log("将字符串转为 Hex 字符串:{}", hexStr); // e5a4a7e5aeb6e5a5bdefbc8ce68891e79a84e5908de5ad97e58fab204855414c454920efbc81

// Hex 字符串转换回去
String originStr = Convert.hexToStr(hexStr, CharsetUtil.CHARSET_UTF_8);
Console.log("将 Hex 字符串转换回原先字符串:{}", originStr);

/*
    如果把各种文字编码形容为各地的方言,那么 Unicode 就是世界各国合作开发的一种语言。

    字符串转为 Unicode(统一码 / 万国码 / 单一码),它是为了解决传统字符编码方案弊端(繁杂不统一,导致编码格式不一致引发的乱码问题)

        Unicode 为每种语言中的每个字符设定了 统一 并且 唯一 的 二进制编码
        以满足 跨语言、跨平台 进行 文本转换、处理 的需求
*/
// 将字符串转换成 Unicode
String unicodeStr = Convert.strToUnicode(desc);
Console.log("字符串转换成 Unicode 串:{}", unicodeStr); // \u5927\u5bb6\u597d\uff0c\u6211\u7684\u540d\u5b57\u53eb HUALEI \uff01

字符编码方式转换(编码与解码)


String a = "我不是乱码";
// 使用 UTF8 字符编码解码为 ISO_8859_1 编码方式的字符串
String mess = Convert.convertCharset(a, CharsetUtil.UTF_8, CharsetUtil.ISO_8859_1);
Console.log("ISO_8859_1 编码方式的中文乱码:{}", mess); // 转换后 mess 为乱码 => 我不是乱码

// 使用 ISO_8859_1 字符编码解码为原先的 UTF8 编码方式的字符串,将乱码转为正确的编码方式:
String raw = Convert.convertCharset(mess, CharsetUtil.ISO_8859_1, "UTF-8");
Console.log("将乱码转为正确的编码后:{}", raw); // 我不是乱码

中文大/小写数字、金额相关转换


// 金额转中文表达(最多保留到小数点后两位)
double money = 18.88D;
String chineseWord = Convert.digitToChinese(money);
Console.log("金额转中文表达:{}", chineseWord); // 壹拾捌元捌角捌分

// 金额转英文表达(最多保留到小数点后两位)
String englishWord = Convert.numberToWord(money);
Console.log("金额转英文表达:{}", englishWord); // EIGHTEEN AND CENTS EIGHTY EIGHT ONLY

double amount = 102389942.32D;
// 数字转中文大写(例:壹仟,最多保留到小数点后两位) isUseTraditional => true
String traditionalChinese = Convert.numberToChinese(amount, true);
// 数字转中文小写(例:一千) isUseTraditional => false
String nonTraditionalChinese = Convert.numberToChinese(amount, false);
Console.log("数字转中文大写,传统:{};非传统:{}", traditionalChinese, nonTraditionalChinese); // 传统:壹亿零贰佰叁拾捌万玖仟玖佰肆拾贰点叁贰;非传统:一亿零二百三十八万九千九百四十二点三二

// 中文大写壹仟叁佰转成纯数字
Console.log("中文大写壹仟叁佰转成数字:{}", Convert.chineseToNumber("壹仟叁佰")); // 1300

// 数字简化 1000 => 1k; 10000 => 1w(最多保留到小数点后两位)
String simple = Convert.numberToSimple(amount);
Console.log("{} 简化为:{}", amount, simple); // 10238.99w

convertTime(目标值,目标值时间单位,转换后的时间单位) 方法主要用于转换时长单位,比如一个很大的毫秒,我想获得这个毫秒数换算成多少天:


// 毫秒数
long ms = 100000000L;
long msToDays = Convert.convertTime(ms, TimeUnit.MILLISECONDS, TimeUnit.DAYS);
Console.log("{} 毫秒约等于 {} 天", ms, msToDays); // 100000000 毫秒约等于 1 天

原始类型和包装类型之间的转换


// Integer 包装类
Class<Integer> wrapClass = Integer.class;

// Integer 去除包装类 => int
Class<?> unWraped = Convert.unWrap(wrapClass); // int
Console.log("Integer 包装类去包装化:{}", unWraped);

// 原始类
Class<?> primitiveClass = long.class;

// 将原始类型转换成包装类
Class<?> wraped = Convert.wrap(primitiveClass); // class java.lang.Long
Console.log("long 原始类包装化:{}", wraped);

DateUtil DateTime 日期时间工具类

对于 java.util.Date 对象是不是还停留在被 Thu Nov dd HH:mm:ss CST yyyy 支配的恐惧中?数字英文单词缩写的混杂让人看起来非常不舒服,如果你想将其转换成 yyyy-MM-dd / yyyy-MM-dd HH:mm:ss 时间格式字符串,你是不是得先 new 一个 SimpleDateFormat,通过 pattern 参数进行初始化格式化器,时间格式不熟练的可能还要百度一下,甚是麻烦。

为了便捷,Hutool 工具使用了一个 DateTime 类来替代之,继承自 Date ,重写了 toString() 方法,直接放回 yyyy-MM-dd HH:mm:ss 形式的字符串,方便在输出时的调用(例如日志记录等),提供了众多便捷的方法对日期对象操作。

代码实例:


// 计时器,计算被其包裹的代码片段执行时间
final TimeInterval timeInterval = DateUtil.timer();

// 根据 birthday 计算现在的年龄
String dateStr = "2000年05月26日";
Date birthday = Convert.toDate(dateStr);
// 可以通过 DatePattern 指定 dateStr 日期时间格式进行解析,如果没有第二个参数,则会自动寻找合适的格式(支持多种格式)进行解析
// DateTime dateTime = new DateTime(dateStr, DatePattern.CHINESE_DATE_PATTERN);
int age = DateUtil.ageOfNow(birthday); // 或 DateUtil.ageOfNow(dateTime);
Console.log("{} 出生的小伙子,如今已经 {} 岁了!", dateStr, age);

// 获取当前时间,格式:yyyy-MM-dd HH:mm:ss => DateTime 类型
Date currentDate = DateUtil.date(); // 2021-11-02 08:57:54
// 等价于 DateTime currentDateTime = new DateTime();
// 不同于 new Date() => Tue Nov 02 08:57:54 CST 2021
Console.log("DateUtil.date() 得到当前时间(yyyy-MM-dd HH:mm:ss):", currentDate);

// 将特殊的日期字符串转换为 DateTime 对象
String strDate2 = "2021年11月02日 12时03分29秒";
// 根据格式化模版将字符串日期转换为 Date 对象
Console.log("Use DateUtil.parse() method convert to Date: {}", DateUtil.parse(strDate2, "yyyy年MM月dd日 HH时mm分ss秒")); // 2021-11-02 12:03:29
// 等价于 new DateTime(strDate2);

// 方式一:当前毫秒数转换为 DateTime 时间,格式:yyyy-MM-dd HH:mm:ss
Date msToDateTime1 = DateUtil.date(Calendar.getInstance()); // 2021-11-02 08:57:54
Console.log("当前毫秒数转换为 DateTime 时间,方式一:", msToDateTime1);

// 方式二:当前毫秒数转换成 DateTime 时间,格式:yyyy-MM-dd HH:mm:ss
Date msToDateTime2 = DateUtil.date(System.currentTimeMillis()); // 2021-11-02 08:57:54
Console.log("当前毫秒数转换为 DateTime 时间,方式二:", msToDateTime2);

// 当前日期字符串,格式:yyyy-MM-dd
String today= DateUtil.today(); // 2021-11-02
Console.log("当前日期字符串,格式:yyyy-MM-dd:{}", today);

// 将 Date 对象格式化为日期字符串,格式:yyyy-MM-dd
String formatDateStr = DateUtil.format(new Date(), "yyyy-MM-dd");
Console.log("Use DateUtil.format() method convert to Date: {}", formatDateStr);

// 获取日期时间(DateTime)的 Time 字符串,格式:HH:mm:ss
String formatTimeStr = DateUtil.formatTime(new DateTime());
Console.log("Use DateUtil.formatTime() method only covert Date to HH:mm:ss:{}", formatTimeStr);

// 星座(zodiac sign)
String zodiac = DateUtil.getZodiac(DateUtil.month(birthday), ((int) DateUtil.betweenDay(DateUtil.beginOfMonth(birthday), birthday, false)));
// 属相(chinese zodiac)
String chineseZodiac = DateUtil.getChineseZodiac(DateUtil.year(birthday));
Console.log("{} 生日的人,星座是:{},属 {} 的。", birthday, zodiac, chineseZodiac);

// 判断今年是不是平闰年
boolean isLeapYear = DateUtil.isLeapYear(DateUtil.year(new Date()));
Console.log("{}年是闰年吗?{}", DateUtil.year(new Date()), isLeapYear);

// 得到一个美化过后的花费时间,即加上中文单位
String spendTimePretty = timeInterval.intervalPretty();
// 运行时间转换为秒为单位
long sec = timeInterval.intervalSecond();

// 返回花费时间,并重置开始时间
long spendTimeAndRestart = timeInterval.intervalRestart();

Console.log("测试运行时间(无单位不美化):", spendTimeAndRestart);
Console.log("测试运行时间(有单位带美化):", spendTimePretty);
Console.log("测试运行时间,转换为秒:{}", sec);

获取指定天/周/月/季度/年的开始/结束时间,返回 DateTime,格式为:yyyy-MM-dd HH:mm:ss


DateTime beginOfDayTime = DateUtil.beginOfDay(new Date());
Console.log("今天开始时间:{}", beginOfDayTime); // 2021-11-03 00:00:00

DateTime beginOfYearTime = DateUtil.beginOfYear(new Date());
Console.log("今年开始时间:{}", beginOfYearTime); // 2021-01-01 00:00:00

DateTime endOfYearTime = DateUtil.endOfYear(new Date());
Console.log("今年结束时间:{}", endOfYearTime); // 2021-12-31 23:59:59

日期时间偏移:即对某个日期增减分、小时、天等等


// 将当前时间增加半个小时
DateTime halfHourLater = DateUtil.offset(currentDate, DateField.MINUTE, 30);
// 等同于:DateUtil.offsetHour(currentDate, 30);
Console.log("当前时间是 {},半个小时后是:{}", currentDate, halfHourLater);
// 五天后的现在时间
DateTime fiveDaysFromNow = DateUtil.offsetDay(currentDate, 5);

Console.log("当前时间是 {},五天后是:{}", currentDate, fiveDaysFromNow);
// 针对当前时间,提供了更为简化的偏移方法
Console.log("昨天的现在是:{}", DateUtil.yesterday());
Console.log("明天的现在是:{}", DateUtil.tomorrow());
Console.log("上周天的现在是:{}", DateUtil.lastWeek());
Console.log("下周的现在是:{}", DateUtil.nextWeek());
Console.log("上个月的现在是:{}", DateUtil.lastMonth());
Console.log("下个月的现在是:{}", DateUtil.nextMonth());

计算两个时间的间隔


// 当前时间
DateTime currentDateTime = new DateTime();

String dateTimeStrStart = "1949/10/01";
Date startTime = Convert.toDate(dateTimeStrStart);
long betweenDays = DateUtil.between(startTime, new DateTime(), DateUnit.DAY);
Console.log("距离新中国成立,已经过了 {} 天", betweenDays);

// 下班时间(当天 17:00:00 )
Date getOffTime = DateUtil.offsetHour(DateUtil.beginOfDay(currentDateTime), 17);
Console.log("下班时间是:{}", getOffTime);
long distance = DateUtil.between(getOffTime, currentDateTime, DateUnit.SECOND, true);

// 间隔时间设置为秒
Console.log("距离下班还剩 {} 秒", distance);
// 格式化间隔时间
Console.log("距离下班还剩 {} 精确到秒", DateUtil.formatBetween(getOffTime, currentDateTime, BetweenFormatter.Level.SECOND));

ObjectUtil 对象信息打印类

针对 Object 通用的工具类方法,不区分是 String 还是 ObjectArray 还是 Collection

代码实例:

判断 null / blank string

final String blankStr = "   ";
final String nullStr = null;
final String dateStr = null;

// isEmpty/isNull 只有为 null 才返回 true
System.out.println(ObjectUtil.isEmpty(blankStr)); // false
System.out.println(ObjectUtil.isEmpty(nullStr)); // true
System.out.println(ObjectUtil.isNull(nullStr)); // true
System.out.println(ObjectUtil.isNull(blankStr)); // false

// null/空格字符串都是 blank
System.out.println(ObjectUtil.defaultIfBlank(blankStr, "目标字符串为空,我是默认")); // 走 defaultValue
System.out.println(ObjectUtil.defaultIfBlank(nullStr, "目标字符串为空,我是默认")); // 走 defaultValue

// 此处判断如果dateStr为null,则调用`Instant.now()`,不为null则执行`DateUtil.parse`
System.out.println(ObjectUtil.defaultIfNull(dateStr, () -> DateUtil.parse(dateStr, DatePattern.NORM_DATETIME_PATTERN).toInstant(), Instant.now()));

System.out.println(ObjectUtil.defaultIfNull(nullStr, "目标字符串为 null ,我是默认")); // 走 defaultValue
System.out.println(ObjectUtil.defaultIfNull(blankStr, "目标字符串为 null ,我是默认")); // blankStr

注意: ObjectUtil.isNull() 方法不能判断对象中字段为空的情况,如果需要检查Bean对象中字段是否全空,需使用 BeanUtil.isEmpty() 方法进行判定。

判断两个对象是否相等,等价于 Objects.equals() 方法


ObjectUtil.equals(nullStr, dateStr); // true
ObjectUtil.equals("HUALIE", "HUALEI"); // true

// 等价于
Objects.equals(nullStr, dateStr); // true

计算对象长度


int[] array = new int[]{1,2,3,4,5};
// 计算数组长度
int length = ObjectUtil.length(array); // 5

Map<String, String> map = new HashMap<>();
map.put("a", "a1");
map.put("b", "b1");
map.put("c", "c1");

// 计算 Map 大小
length = ObjectUtil.length(map); // 3

如果是字符串调用其 length() 方法,集合类调用其 size() 方法,数组调用其length 属性,其他可遍历对象遍历计算长度。

StrUtil 处理字符串工具

image.png

↑ 和这个类类似,但是相较于 StringUtils 这个类写起来更短,更能偷懒 [狗头],类中常用的方法有 isBlank()isNotBlank()isEmpty()isNotEmpty() 主要用于对字符串的判空,这里就不赘述了。

代码实例:


// 去除前缀
String[] tableNames = {"tb_user", "t_mall", "province", "t_capital", "tb_task"};
for (int i = 0; i < tableNames.length; i++) {
    if (StrUtil.containsAnyIgnoreCase(tableNames[i], "tb_", "t_")) {
        // 去除前缀忽略大小写
        tableNames[i] = StrUtil.removePrefixIgnoreCase(tableNames[i], "tb_");
        // 去除前缀忽略大小写
        tableNames[i] = StrUtil.removePrefixIgnoreCase(tableNames[i], "t_");
    }
}
Console.log("移除表名前缀后:{}", (Object) tableNames); // [user, mall, province, capital, task]

// 去除后缀
String fileName = StrUtil.removeSuffix("HUALEI.png", ".png");
Console.log("去除文件.扩展名后:{}", fileName); // HUALEI

// 字符创常量,包括点、空字符串、换行符,还有一些 HTML 中的转义字符
System.out.println(StrUtil.AT.concat("HUALEI").concat(StrUtil.DOT).concat(StrUtil.LF)); // @HUALEI.

// 使用字符串模板代替字符串拼接
String template = "{}爱{},我爱你,蜜雪冰城{}";
String str = StrUtil.format(template, "你", "我", "甜蜜蜜"); // 你爱我,我爱你,蜜雪冰城甜蜜蜜

CollUtil | CollStreamUtil 集合处理工具类

这两个类主要封装的是对数组、列表等集合类的操作方法,集合在开发中占很大一部分,必须重点掌握,以提高我们的开发效率。

代码实例:

模拟数据库排序分页,返回一个 PageResult


List<Fruit> fruits = fruitMapper.selectList(Wrappers.emptyWrapper());

// 总记录数
long totalCount = fruits.size();
// 当前页
int currentPage = 1;
// 每页记录数
int pageSize = 10;

// 当前页索引,从 0 开始,表示第一页
int pageNo = currentPage - 1;

// 默认分页列表
List<Fruit> defaultPageList = CollUtil.page(pageNo, pageSize, fruits);

// 按照记录更新时间进行排序分页列表
List<Fruit> orderPageList = CollUtil.sortPageAll(pageNo, pageSize, Comparator.comparing(Fruit::getUpdateTime), fruits);

// 重新用分页参数及记录列表组装 PageResult 分页结果对象
PageResult pageResult = new PageResult(orderPageList, totalCount, pageSize, currentPage));

System.out.println("当前页数:" + pageResult.getCurrPage());
System.out.println("每页记录数:" + pageResult.getPageSize());
System.out.println("总记录数:" + pageResult.getTotalCount());
System.out.println("总页数:" + pageResult.getTotalPage());
// 当前页记录列表
pageResult.getList().forEach(System.out::println);

获取表头别名映射


// 字段名列表
List<String> headerNameList = new ArrayList<>();

// 使用反射工具获取全部表头名
for (Field field : ReflectUtil.getFields(ProvinceExcelVO.class)) {
    if (!StrUtil.equals(field.getName(), "serialVersionUID")) {
        headerNameList.add(field.getName());
    }
}
// 表头别名列表
List<String> aliasList = CollUtil.newArrayList("省份名称", "简称", "面积(km²)", "人口数量(万)", "著名景点", "邮政编码", "省会城市", "别称", "气候类型", "车牌号");

// 一一对应,形成表头别名映射
Map<String, String> headerAliasMap = CollUtil.zip(fieldNameList, aliasList);

建立省名和省份对象之间的 Map


// 省份表中所有省份信息
List<Province> provinces = provinceMapper.selectList(Wrappers.emptyWrapper());

// 使用 CollStreamUtil 集合流工具,一行代码解决
Map<String, Province> provinceMap = CollStreamUtil.toIdentityMap(provinces, Province::getProvince);

// 等价于
Map<String, List<Province>> provinceMap =
        provinces.stream()
                .collect(Collectors.toMap(Province::getProvince, Arrays::asList));

// 又等价于,结合 Convert 方法二:
Map<String, List<Province>> provinceMap =
        provinces.stream()
                .collect(Collectors.toMap(Province::getProvince, p -> Convert.toList(Province.class, p)));

// 获取江西省省份信息
Console.log("江西省省份信息为:{}", provinceMap.get("江西省"));

将所有省份按照主键 pid 进行分组


Map<Integer, List<Province>> groupByKey = CollStreamUtil.groupByKey(CollUtil.unionAll(provinces, provinces), Province::getPid);

// 等价于
Map<Integer, List<Province>> groupByKey = CollUtil.unionAll(provinces, provinces).stream()
                .collect(Collectors.groupingBy(Province::getPid, Collectors.toList()));

提取省份信息列表中省份名

        
List<String> provinceNameList = CollStreamUtil.toList(provinces, Province::getProvince);

// 等价于
List<String> provinceNameList = provinces.stream()
        .map(Province::getProvince)
        .collect(Collectors.toList());

两个 map 根据 key 进行合并(merge)操作,最终结果期望为:(key: pid, value: province(省份名) + "-" + abbr(省份简称))


// (key: pid, value: province)             
Map<Integer, String> pidToProvince = CollStreamUtil.toMap(provinces, Province::getPid, Province::getProvince);

// 等价于
Map<Integer, String> pidToProvince =
        provinces.stream()
                .collect(Collectors.toMap(Province::getPid, Province::getProvince));
                
// (key: pid, value: abbr)      
Map<Integer, String> pidToAbbr = CollStreamUtil.toMap(provinces, Province::getPid, Province::getAbbr);

// 等价于
Map<Integer, String> pidToProvince =
        provinces.stream()
                .collect(Collectors.toMap(Province::getPid, Province::getAbbr));

// 根据 key 合并 Map,key 相同则走 merge 规则
Map<Integer, String> mergeResult = CollStreamUtil.merge(pidToProvince, pidToAbbr, (province, abbr) -> province + "-" + abbr);

RandomUtil 随机工具

RandomUtil 工具类主要是对 Random 对象的封装,用于产生随机数/列表,虽然是伪随机数,但是对于大多数使用场景还是够用的。

代码实例:


// 生成指定范围内的随机整数
for (int i = 0; i < 10; i++) {
    System.out.println(RandomUtil.randomInt(0, 100));
}

List<String> randomList = new ArrayList<>();
// 生成随机字符串(只包含数字和字符)
for (int i = 0; i < 10; i++) {
    randomList.add(RandomUtil.randomString(5));
}

// 从指定列表中随机取 5 个数
List<Integer> eleList = RandomUtil.randomEleList(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 5);

// 随机从字符串列表中取出 5 个,不重复(取出的个数不能超过列表的大小)
Set<String> eleSet = RandomUtil.randomEleSet(randomList, 5);

// 权重随机生成器,传入带权重的对象,然后根据权重随机获取对象
WeightRandom weightRandom = RandomUtil.weightRandom(new WeightRandom.WeightObj[]{new WeightRandom.WeightObj("HUA", 60), new WeightRandom.WeightObj("L", 10), new WeightRandom.WeightObj("EI", 30)});

// 随机取 10 次,"HUA" 字符串出现的次数是最多的
for (int i = 0; i < 10; i++) {
    System.out.println(weightRandom.next());
}

ExcelUtil Excel 操作工具类

该类是对 ApachePOI 库的二次封装,便捷了对 MS Office 文档操作,使用该类时,需要用户执行导入 poi-ooxml 依赖,这个包会自动关联引入 poi 包,可以很好的支持 Office2007+ 的文档格式。

注意

开始使用前,需导入依赖:

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>${poi.version}</version>
</dependency>

未引入使用其中的工具方法,会报错:

You need to add dependency of 'poi-ooxml' to your project, and version >= 4.1.2

代码实例:

// 写入 Excel 中的数据源
List<Province> provinces = provinceMapper.selectList(Wrappers.emptyWrapper());

// 写出文件路径
String inputPath = "D:/桌面/导出省份信息.xlsx";
// 通过工具类创建一个 Writer 写入对象
ExcelWriter excelWriter = ExcelUtil.getWriter(inputPath, "省份信息表");

// 自定义表头名(方法一)
excelWriter.addHeaderAlias("pid", "省份ID");
excelWriter.addHeaderAlias("province", "省份");
excelWriter.addHeaderAlias("abbr", "简称");
excelWriter.addHeaderAlias("area", "面积(km²)");
excelWriter.addHeaderAlias("population", "人口数量(万)");
excelWriter.addHeaderAlias("attraction", "著名景点");
excelWriter.addHeaderAlias("capital", "省会");

// 自定义表头名(方法二)
Map<String, String> headerAlias = new LinkedHashMap<>(7);

for (Field field : ReflectUtil.getFields(Province.class)) {
    String name = field.getName();
    String alias;
    switch (name) {
        case "pid":
            alias = "省份ID";
            break;
        case "province":
            alias = "省份";
            break;
        case "abbr":
            alias = "简称";
            break;
        case "area":
            alias = "面积(km²)";
            break;
        case "population":
            alias = "人口数量(万)";
            break;
        case "attraction":
            alias = "著名景点";
            break;
        case "capital":
            alias = "省会";
            break;
        default:
            alias = null;
    }
    if (ObjectUtil.isNotEmpty(alias)) {
        headerAlias.put(field.getName(), alias);
    }
}

// 使用无序的 HashMap 入参表头也跟着无序,需使用 LinkedHashMap ,保证有序
excelWriter.setHeaderAlias(headerAlias);

// 写出数据源到 Excel 当中,使用默认样式,强制输出标题
excelWriter.write(provinces, true);

// 关闭 writer ,释放内存
excelWriter.close();

结尾

撰文不易,欢迎大家点赞、评论,你的关注、点赞是我坚持的不懈动力,感谢大家能够看到这里!Peace & Love。

参考

Overview (hutool-码云(gitee.com))