diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml index 7db304dc5..4fa5a70ed 100755 --- a/hutool-core/pom.xml +++ b/hutool-core/pom.xml @@ -15,9 +15,9 @@ hutool-core ${project.artifactId} Hutool核心,包括集合、字符串、Bean等工具 - + cn.hutool.core - + \ No newline at end of file diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java index 3ac2129fd..531378f2a 100755 --- a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java @@ -363,7 +363,7 @@ public class CollUtil { * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
* 结果:[a, b, c],此结果中只保留了一个c * - * @param 集合元素类型 + * @param 集合元素类型 * @param colls 集合列表 * @return 交集的集合,返回 {@link LinkedHashSet} * @since 5.3.9 @@ -1735,21 +1735,7 @@ public class CollUtil { list.sort(comparator); } - return page(pageNo, pageSize, list); - } - - /** - * 对指定List分页取值 - * - * @param 集合元素类型 - * @param pageNo 页码,从0开始计数,0表示第一页 - * @param pageSize 每页的条目数 - * @param list 列表 - * @return 分页后的段落内容 - * @since 4.1.20 - */ - public static List page(final int pageNo, final int pageSize, final List list) { - return ListUtil.page(pageNo, pageSize, list); + return ListUtil.page(list, pageNo, pageSize); } /** @@ -2015,7 +2001,7 @@ public class CollUtil { } /** - * 根据元素的指定字段名分组,非Bean都放在第一个分组中 + * 根据元素的指定字段值分组,非Bean都放在第一个分组中 * * @param 元素类型 * @param collection 集合 @@ -2023,8 +2009,24 @@ public class CollUtil { * @return 分组列表 */ public static List> groupByField(final Collection collection, final String fieldName) { + return groupByFunc(collection, t -> BeanUtil.getFieldValue(t, fieldName)); + } + + /** + * 根据元素的指定字段值分组,非Bean都放在第一个分组中
+ * 例如:{@code + * CollUtil.groupByFunc(list, TestBean::getAge) + * } + * + * @param 元素类型 + * @param collection 集合 + * @param getter getter方法引用 + * @return 分组列表 + * @since 6.0.0 + */ + public static List> groupByFunc(final Collection collection, final Function getter) { return group(collection, new Hash32() { - private final List fieldNameList = new ArrayList<>(); + private final List hashValList = new ArrayList<>(); @Override public int hash32(final T t) { @@ -2032,14 +2034,13 @@ public class CollUtil { // 非Bean放在同一子分组中 return 0; } - final Object value = FieldUtil.getFieldValue(t, fieldName); - final int hash = fieldNameList.indexOf(value); + final Object value = getter.apply(t); + int hash = hashValList.indexOf(value); if (hash < 0) { - fieldNameList.add(value); - return fieldNameList.size() - 1; - } else { - return hash; + hashValList.add(value); + hash = hashValList.size() - 1; } + return hash; } }); } diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java index d2cb0d700..d05b98999 100755 --- a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java @@ -8,20 +8,11 @@ import cn.hutool.core.collection.partition.RandomAccessPartition; import cn.hutool.core.comparator.PinyinComparator; import cn.hutool.core.comparator.PropertyComparator; import cn.hutool.core.lang.Assert; +import cn.hutool.core.math.PageInfo; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.PageUtil; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.RandomAccess; +import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; import java.util.function.Predicate; @@ -254,42 +245,53 @@ public class ListUtil { * 对指定List分页取值 * * @param 集合元素类型 - * @param pageNo 页码,第一页的页码取决于{@link PageUtil#getFirstPageNo()},默认0 + * @param pageNo 页码,第一页的页码,从0开始 * @param pageSize 每页的条目数 * @param list 列表 * @return 分页后的段落内容 * @since 4.1.20 */ - public static List page(final int pageNo, final int pageSize, final List list) { + public static List page(final List list, final int pageNo, final int pageSize) { + if (CollUtil.isEmpty(list)) { + return new ArrayList<>(0); + } + return page(list, PageInfo.of(list.size(), pageSize) + .setFirstPageNo(0).setPageNo(pageNo)); + } + + /** + * 对指定List分页取值 + * + * @param 集合元素类型 + * @param pageInfo 分页信息 + * @param list 列表 + * @return 分页后的段落内容 + * @since 4.1.20 + */ + public static List page(final List list, final PageInfo pageInfo) { if (CollUtil.isEmpty(list)) { return new ArrayList<>(0); } - final int resultSize = list.size(); - // 每页条目数大于总数直接返回所有 - if (resultSize <= pageSize) { - if (pageNo < (PageUtil.getFirstPageNo() + 1)) { + final int total = list.size(); + final int pageSize = pageInfo.getPageSize(); + // 不满一页 + if (total <= pageSize) { + if (pageInfo.isFirstPage()) { + // 页码为1,返回所有 return view(list); } else { // 越界直接返回空 return new ArrayList<>(0); } } - // 相乘可能会导致越界 临时用long - if (((long) (pageNo - PageUtil.getFirstPageNo()) * pageSize) > resultSize) { + + if (pageInfo.getBeginIndex() > total) { // 越界直接返回空 return new ArrayList<>(0); } - final int[] startEnd = PageUtil.transToStartEnd(pageNo, pageSize); - if (startEnd[1] > resultSize) { - startEnd[1] = resultSize; - if (startEnd[0] > startEnd[1]) { - return new ArrayList<>(0); - } - } - - return sub(list, startEnd[0], startEnd[1]); + return sub(list, pageInfo.getBeginIndex(), pageInfo.getEndIndexExclude()); } /** @@ -307,16 +309,11 @@ public class ListUtil { } final int total = list.size(); - final int totalPage = PageUtil.totalPage(total, pageSize); - for (int pageNo = PageUtil.getFirstPageNo(); pageNo < totalPage + PageUtil.getFirstPageNo(); pageNo++) { - // 获取当前页在列表中对应的起止序号 - final int[] startEnd = PageUtil.transToStartEnd(pageNo, pageSize); - if (startEnd[1] > total) { - startEnd[1] = total; - } - + final PageInfo pageInfo = PageInfo.of(total, pageSize); + while(pageInfo.isValidPage()){ // 返回数据 - pageListConsumer.accept(sub(list, startEnd[0], startEnd[1])); + pageListConsumer.accept(sub(list, pageInfo.getBeginIndex(), pageInfo.getEndIndexExclude())); + pageInfo.nextPage(); } } @@ -346,7 +343,7 @@ public class ListUtil { if (CollUtil.isEmpty(list)) { return list; } - if(null == c){ + if (null == c) { c = Comparator.nullsFirst((Comparator) Comparator.naturalOrder()); } list.sort(c); @@ -446,10 +443,10 @@ public class ListUtil { /** * 在指定位置设置元素。当index小于List的长度时,替换指定位置的值,否则追加{@code paddingElement}直到到达index后,设置值 * - * @param 元素类型 - * @param list List列表 - * @param index 位置 - * @param element 新元素 + * @param 元素类型 + * @param list List列表 + * @param index 位置 + * @param element 新元素 * @param paddingElement 填充的值 * @return 原List * @since 5.8.4 @@ -471,29 +468,29 @@ public class ListUtil { /** * 截取集合的部分 * - * @param 集合元素类型 - * @param list 被截取的数组 - * @param start 开始位置(包含) - * @param end 结束位置(不包含) + * @param 集合元素类型 + * @param list 被截取的数组 + * @param begionInclude 开始位置(包含) + * @param endExclude 结束位置(不包含) * @return 截取后的数组,当开始位置超过最大时,返回空的List */ - public static List sub(final List list, final int start, final int end) { - return sub(list, start, end, 1); + public static List sub(final List list, final int begionInclude, final int endExclude) { + return sub(list, begionInclude, endExclude, 1); } /** * 截取集合的部分
* 此方法与{@link List#subList(int, int)} 不同在于子列表是新的副本,操作子列表不会影响原列表。 * - * @param 集合元素类型 - * @param list 被截取的数组 - * @param start 开始位置(包含) - * @param end 结束位置(不包含) - * @param step 步进 + * @param 集合元素类型 + * @param list 被截取的数组 + * @param begionInclude 开始位置(包含) + * @param endExclude 结束位置(不包含) + * @param step 步进 * @return 截取后的数组,当开始位置超过最大时,返回空的List * @since 4.0.6 */ - public static List sub(final List list, int start, int end, int step) { + public static List sub(final List list, int begionInclude, int endExclude, int step) { if (list == null) { return null; } @@ -503,25 +500,25 @@ public class ListUtil { } final int size = list.size(); - if (start < 0) { - start += size; + if (begionInclude < 0) { + begionInclude += size; } - if (end < 0) { - end += size; + if (endExclude < 0) { + endExclude += size; } - if (start == size) { + if (begionInclude == size) { return new ArrayList<>(0); } - if (start > end) { - final int tmp = start; - start = end; - end = tmp; + if (begionInclude > endExclude) { + final int tmp = begionInclude; + begionInclude = endExclude; + endExclude = tmp; } - if (end > size) { - if (start >= size) { + if (endExclude > size) { + if (begionInclude >= size) { return new ArrayList<>(0); } - end = size; + endExclude = size; } if (step < 1) { @@ -529,7 +526,7 @@ public class ListUtil { } final List result = new ArrayList<>(); - for (int i = start; i < end; i += step) { + for (int i = begionInclude; i < endExclude; i += step) { result.add(list.get(i)); } return result; @@ -719,8 +716,8 @@ public class ListUtil { * 通过删除或替换现有元素或者原地添加新的元素来修改列表,并以列表形式返回被修改的内容。此方法不会改变原列表。 * 类似js的splice函数 * - * @param 元素类型 - * @param list 列表 + * @param 元素类型 + * @param list 列表 * @param start 指定修改的开始位置(从 0 计数), 可以为负数, -1代表最后一个元素 * @param deleteCount 删除个数,必须是正整数 * @param items 放入的元素 diff --git a/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java index efec80bd6..6c042e540 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/CalendarUtil.java @@ -2,12 +2,12 @@ package cn.hutool.core.date; import cn.hutool.core.comparator.CompareUtil; import cn.hutool.core.convert.NumberChineseFormatter; +import cn.hutool.core.date.format.GlobalCustomFormat; import cn.hutool.core.date.format.parser.DateParser; import cn.hutool.core.date.format.parser.FastDateParser; -import cn.hutool.core.date.format.GlobalCustomFormat; import cn.hutool.core.date.format.parser.PositionDateParser; -import cn.hutool.core.util.ObjUtil; import cn.hutool.core.text.StrUtil; +import cn.hutool.core.util.ObjUtil; import javax.xml.datatype.XMLGregorianCalendar; import java.text.ParsePosition; @@ -15,7 +15,6 @@ import java.time.Instant; import java.time.LocalDateTime; import java.util.Calendar; import java.util.Date; -import java.util.LinkedHashSet; import java.util.Locale; import java.util.TimeZone; @@ -74,7 +73,7 @@ public class CalendarUtil { /** * 转换为Calendar对象 * - * @param millis 时间戳 + * @param millis 时间戳 * @param timeZone 时区 * @return Calendar对象 * @since 5.7.22 @@ -105,6 +104,8 @@ public class CalendarUtil { return Calendar.PM == calendar.get(Calendar.AM_PM); } + // region ----- modify 时间修改 + /** * 修改日期为某个时间字段起始时间 * @@ -355,6 +356,7 @@ public class CalendarUtil { public static Calendar endOfYear(final Calendar calendar) { return ceiling(calendar, DateField.YEAR); } + // endregion /** * 比较两个日期是否为同一天 @@ -375,8 +377,8 @@ public class CalendarUtil { /** * 比较两个日期是否为同一周 * - * @param cal1 日期1 - * @param cal2 日期2 + * @param cal1 日期1 + * @param cal2 日期2 * @param isMon 是否为周一。国内第一天为星期一,国外第一天为星期日 * @return 是否为同一周 * @since 5.7.21 @@ -443,28 +445,6 @@ public class CalendarUtil { return date1.getTimeInMillis() == date2.getTimeInMillis(); } - /** - * 获得指定日期区间内的年份和季度
- * - * @param startDate 起始日期(包含) - * @param endDate 结束日期(包含) - * @return 季度列表 ,元素类似于 20132 - * @since 4.1.15 - */ - public static LinkedHashSet yearAndQuarter(long startDate, final long endDate) { - final LinkedHashSet quarters = new LinkedHashSet<>(); - final Calendar cal = calendar(startDate); - while (startDate <= endDate) { - // 如果开始时间超出结束时间,让结束时间为开始时间,处理完后结束循环 - quarters.add(yearAndQuarter(cal)); - - cal.add(Calendar.MONTH, 3); - startDate = cal.getTimeInMillis(); - } - - return quarters; - } - /** * 获得指定日期年份和季度
* 格式:[20131]表示2013年第一季度 @@ -637,51 +617,7 @@ public class CalendarUtil { return result.toString(); } - /** - * 计算相对于dateToCompare的年龄,长用于计算指定生日在某年的年龄 - * - * @param birthday 生日 - * @param dateToCompare 需要对比的日期 - * @return 年龄 - */ - protected static int age(final long birthday, final long dateToCompare) { - if (birthday > dateToCompare) { - throw new IllegalArgumentException("Birthday is after dateToCompare!"); - } - - final Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(dateToCompare); - - final int year = cal.get(Calendar.YEAR); - final int month = cal.get(Calendar.MONTH); - final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH); - final boolean isLastDayOfMonth = dayOfMonth == cal.getActualMaximum(Calendar.DAY_OF_MONTH); - - cal.setTimeInMillis(birthday); - int age = year - cal.get(Calendar.YEAR); - //当前日期,则为0岁 - if (age == 0){ - return 0; - } - - final int monthBirth = cal.get(Calendar.MONTH); - if (month == monthBirth) { - - final int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH); - final boolean isLastDayOfMonthBirth = dayOfMonthBirth == cal.getActualMaximum(Calendar.DAY_OF_MONTH); - // issue#I6E6ZG,法定生日当天不算年龄,从第二天开始计算 - if ((false == isLastDayOfMonth || false == isLastDayOfMonthBirth) && dayOfMonth <= dayOfMonthBirth) { - // 如果生日在当月,但是未达到生日当天的日期,年龄减一 - age--; - } - } else if (month < monthBirth) { - // 如果当前月份未达到生日的月份,年龄计算减一 - age--; - } - - return age; - } - + // region ----- parse /** * 通过给定的日期格式解析日期时间字符串。
* 传入的日期格式会逐个尝试,直到解析成功,返回{@link Calendar}对象,否则抛出{@link DateException}异常。 @@ -782,4 +718,50 @@ public class CalendarUtil { return parser.parse(StrUtil.str(str), new ParsePosition(0), calendar) ? calendar : null; } + // endregion + + /** + * 计算相对于dateToCompare的年龄,长用于计算指定生日在某年的年龄 + * + * @param birthday 生日 + * @param dateToCompare 需要对比的日期 + * @return 年龄 + */ + protected static int age(final long birthday, final long dateToCompare) { + if (birthday > dateToCompare) { + throw new IllegalArgumentException("Birthday is after dateToCompare!"); + } + + final Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(dateToCompare); + + final int year = cal.get(Calendar.YEAR); + final int month = cal.get(Calendar.MONTH); + final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH); + final boolean isLastDayOfMonth = dayOfMonth == cal.getActualMaximum(Calendar.DAY_OF_MONTH); + + cal.setTimeInMillis(birthday); + int age = year - cal.get(Calendar.YEAR); + //当前日期,则为0岁 + if (age == 0) { + return 0; + } + + final int monthBirth = cal.get(Calendar.MONTH); + if (month == monthBirth) { + + final int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH); + final boolean isLastDayOfMonthBirth = dayOfMonthBirth == cal.getActualMaximum(Calendar.DAY_OF_MONTH); + // issue#I6E6ZG,法定生日当天不算年龄,从第二天开始计算 + if ((false == isLastDayOfMonth || false == isLastDayOfMonthBirth) && dayOfMonth <= dayOfMonthBirth) { + // 如果生日在当月,但是未达到生日当天的日期,年龄减一 + age--; + } + } else if (month < monthBirth) { + // 如果当前月份未达到生日的月份,年龄计算减一 + age--; + } + + return age; + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DatePattern.java b/hutool-core/src/main/java/cn/hutool/core/date/DatePattern.java index b59eea778..87edc3e2b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/DatePattern.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/DatePattern.java @@ -31,7 +31,7 @@ import java.util.regex.Pattern; *
  • yyyy-MM-dd HH:mm:ss.SSS 示例:2022-08-05 12:59:59.559
  • *
  • yyyy-MM-dd HH:mm:ss.SSSZ 示例:2022-08-05 12:59:59.559+0800【东八区中国时区】、2022-08-05 04:59:59.559+0000【冰岛0时区】, 年月日 时分秒 毫秒 时区
  • *
  • yyyy-MM-dd HH:mm:ss.SSSz 示例:2022-08-05 12:59:59.559UTC【世界标准时间=0时区】、2022-08-05T12:59:59.599GMT【冰岛0时区】、2022-08-05T12:59:59.599CST【东八区中国时区】、2022-08-23T03:45:00.599EDT【美国东北纽约时间,-0400】 ,年月日 时分秒 毫秒 时区
  • - *
  • yyyy-MM-dd'T'HH:mm:ss.SSS'Z' 示例:2022-08-05T12:59:59.559Z, 其中:''单引号表示转义字符,T:分隔符,Z:一般值UTC,0时区的时间含义
  • + *
  • yyyy-MM-dd'T'HH:mm:ss.SSS'Z' 示例:2022-08-05T12:59:59.559Z, 其中:''单引号表示转义字符,T:分隔符,Z:一般指UTC,0时区的时间含义
  • *
  • yyyy-MM-dd'T'HH:mm:ss.SSSZ 示例:2022-08-05T11:59:59.559+0800, 其中:Z,表示时区
  • *
  • yyyy-MM-dd'T'HH:mm:ss.SSSX 示例:2022-08-05T12:59:59.559+08, 其中:X:两位时区,+08表示:东8区,中国时区
  • *
  • yyyy-MM-dd'T'HH:mm:ss.SSSXX 示例:2022-08-05T12:59:59.559+0800, 其中:XX:四位时区
  • @@ -61,7 +61,7 @@ import java.util.regex.Pattern; * 如:“09:30 UTC”表示为“09:30Z”或“T0930Z”,其中:Z 是 +00:00 的缩写,意思是 UTC(零时分秒的偏移量). *

    *
      - *
    • yyyy-MM-dd'T'HH:mm:ssZ
    • + *
    • yyyy-MM-dd'T'HH:mm:ss'Z'
    • *
    • 2022-08-23T15:20:46UTC
    • *
    • 2022-08-23T15:20:46 UTC
    • *
    • 2022-08-23T15:20:46+0000
    • @@ -189,15 +189,15 @@ public class DatePattern { /** * ISO8601日期时间格式,精确到毫秒:yyyy-MM-dd HH:mm:ss,SSS */ - public static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS"; + public static final String NORM_DATETIME_COMMA_MS_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS"; /** * ISO8601日期时间格式,精确到毫秒 {@link FastDateFormat}:yyyy-MM-dd HH:mm:ss,SSS */ - public static final FastDateFormat ISO8601_FORMAT = FastDateFormat.getInstance(ISO8601_PATTERN); + public static final FastDateFormat NORM_DATETIME_COMMA_MS_FORMAT = FastDateFormat.getInstance(NORM_DATETIME_COMMA_MS_PATTERN); /** * 标准日期格式 {@link DateTimeFormatter}:yyyy-MM-dd HH:mm:ss,SSS */ - public static final DateTimeFormatter ISO8601_FORMATTER = createFormatter(ISO8601_PATTERN); + public static final DateTimeFormatter NORM_DATETIME_COMMA_MS_FORMATTER = createFormatter(NORM_DATETIME_COMMA_MS_PATTERN); /** * 标准日期格式:yyyy年MM月dd日 @@ -286,8 +286,7 @@ public class DatePattern { .toFormatter(); // endregion - // region Others - //================================================== Others ================================================== + // region ----- Others /** * HTTP头中日期时间格式:EEE, dd MMM yyyy HH:mm:ss z */ @@ -307,76 +306,78 @@ public class DatePattern { public static final FastDateFormat JDK_DATETIME_FORMAT = FastDateFormat.getInstance(JDK_DATETIME_PATTERN, Locale.US); /** - * UTC时间:yyyy-MM-dd'T'HH:mm:ss + * ISO8601日期时间:yyyy-MM-dd'T'HH:mm:ss
      + * 按照ISO8601规范,默认使用T分隔日期和时间,末尾不加Z表示当地时区 */ - public static final String UTC_SIMPLE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"; + public static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"; /** - * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss + * ISO8601日期时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss */ - public static final FastDateFormat UTC_SIMPLE_FORMAT = FastDateFormat.getInstance(UTC_SIMPLE_PATTERN, TimeZone.getTimeZone("UTC")); + public static final FastDateFormat ISO8601_FORMAT = FastDateFormat.getInstance(ISO8601_PATTERN); /** * UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSS */ - public static final String UTC_SIMPLE_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS"; + public static final String ISO8601_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS"; /** * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSS */ - public static final FastDateFormat UTC_SIMPLE_MS_FORMAT = FastDateFormat.getInstance(UTC_SIMPLE_MS_PATTERN, TimeZone.getTimeZone("UTC")); + public static final FastDateFormat ISO8601_MS_FORMAT = FastDateFormat.getInstance(ISO8601_MS_PATTERN); /** - * UTC时间:yyyy-MM-dd'T'HH:mm:ss'Z' + * UTC时间:yyyy-MM-dd'T'HH:mm:ss'Z'
      + * 按照ISO8601规范,后缀加Z表示UTC时间 */ public static final String UTC_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'"; /** - * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss'Z' + * ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss'Z' */ - public static final FastDateFormat UTC_FORMAT = FastDateFormat.getInstance(UTC_PATTERN, TimeZone.getTimeZone("UTC")); + public static final FastDateFormat UTC_FORMAT = FastDateFormat.getInstance(UTC_PATTERN, ZoneUtil.ZONE_UTC); /** - * UTC时间:yyyy-MM-dd'T'HH:mm:ssZ + * ISO8601时间:yyyy-MM-dd'T'HH:mm:ssZ,Z表示一个时间偏移,如+0800 */ - public static final String UTC_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZ"; + public static final String ISO8601_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZ"; /** - * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssZ + * ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssZ,Z表示一个时间偏移,如+0800 */ - public static final FastDateFormat UTC_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_WITH_ZONE_OFFSET_PATTERN, TimeZone.getTimeZone("UTC")); + public static final FastDateFormat ISO8601_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_WITH_ZONE_OFFSET_PATTERN); /** - * UTC时间:yyyy-MM-dd'T'HH:mm:ssXXX + * ISO8601时间:yyyy-MM-dd'T'HH:mm:ssXXX */ - public static final String UTC_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX"; + public static final String ISO8601_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX"; /** - * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssXXX + * ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ssXXX */ - public static final FastDateFormat UTC_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_WITH_XXX_OFFSET_PATTERN); + public static final FastDateFormat ISO8601_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_WITH_XXX_OFFSET_PATTERN); /** - * UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSS'Z' + * ISO8601时间:yyyy-MM-dd'T'HH:mm:ss.SSS'Z' */ public static final String UTC_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; /** - * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSS'Z' + * ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSS'Z' */ - public static final FastDateFormat UTC_MS_FORMAT = FastDateFormat.getInstance(UTC_MS_PATTERN, TimeZone.getTimeZone("UTC")); + public static final FastDateFormat UTC_MS_FORMAT = FastDateFormat.getInstance(UTC_MS_PATTERN, ZoneUtil.ZONE_UTC); /** - * UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSSZ + * ISO8601时间:yyyy-MM-dd'T'HH:mm:ss.SSSZ */ - public static final String UTC_MS_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; + public static final String ISO8601_MS_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; /** - * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSSZ + * ISO8601时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSSZ */ - public static final FastDateFormat UTC_MS_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_MS_WITH_ZONE_OFFSET_PATTERN, TimeZone.getTimeZone("UTC")); + public static final FastDateFormat ISO8601_MS_WITH_ZONE_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_MS_WITH_ZONE_OFFSET_PATTERN); /** - * UTC时间:yyyy-MM-dd'T'HH:mm:ss.SSSXXX + * ISO8601时间:yyyy-MM-dd'T'HH:mm:ss.SSSXXX */ - public static final String UTC_MS_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"; + public static final String ISO8601_MS_WITH_XXX_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"; /** * UTC时间{@link FastDateFormat}:yyyy-MM-dd'T'HH:mm:ss.SSSXXX */ - public static final FastDateFormat UTC_MS_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(UTC_MS_WITH_XXX_OFFSET_PATTERN); + public static final FastDateFormat ISO8601_MS_WITH_XXX_OFFSET_FORMAT = FastDateFormat.getInstance(ISO8601_MS_WITH_XXX_OFFSET_PATTERN); // endregion /** diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java index dfed3e3db..a20c27a93 100755 --- a/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/DateUtil.java @@ -5,12 +5,7 @@ import cn.hutool.core.comparator.CompareUtil; import cn.hutool.core.date.format.DatePrinter; import cn.hutool.core.date.format.FastDateFormat; import cn.hutool.core.date.format.GlobalCustomFormat; -import cn.hutool.core.date.format.parser.CSTDateParser; -import cn.hutool.core.date.format.parser.NormalDateParser; -import cn.hutool.core.date.format.parser.PositionDateParser; -import cn.hutool.core.date.format.parser.PureDateParser; -import cn.hutool.core.date.format.parser.TimeParser; -import cn.hutool.core.date.format.parser.UTCDateParser; +import cn.hutool.core.date.format.parser.*; import cn.hutool.core.lang.Assert; import cn.hutool.core.math.NumberUtil; import cn.hutool.core.regex.PatternPool; @@ -26,14 +21,7 @@ import java.time.LocalDateTime; import java.time.Year; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.TimeZone; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; @@ -513,21 +501,7 @@ public class DateUtil extends CalendarUtil { return yearAndQuarter(calendar(date)); } - /** - * 获得指定日期区间内的年份和季节
      - * - * @param startDate 起始日期(包含) - * @param endDate 结束日期(包含) - * @return 季度列表 ,元素类似于 20132 - */ - public static LinkedHashSet yearAndQuarter(final Date startDate, final Date endDate) { - if (startDate == null || endDate == null) { - return new LinkedHashSet<>(0); - } - return yearAndQuarter(startDate.getTime(), endDate.getTime()); - } - // ------------------------------------ Format start ---------------------------------------------- - + // region ----- format /** * 格式化日期时间
      * 格式 yyyy-MM-dd HH:mm:ss @@ -696,10 +670,9 @@ public class DateUtil extends CalendarUtil { return CalendarUtil.formatChineseDate(CalendarUtil.calendar(date), withTime); } - // ------------------------------------ Format end ---------------------------------------------- - - // ------------------------------------ Parse start ---------------------------------------------- + // endregion + // region ----- parse /** * 构建DateTime对象 * @@ -840,8 +813,8 @@ public class DateUtil extends CalendarUtil { // Wed Aug 01 00:00:00 CST 2012 return CSTDateParser.INSTANCE.parse(dateStr); } else if (StrUtil.contains(dateStr, 'T')) { - // UTC时间 - return UTCDateParser.INSTANCE.parse(dateStr); + // ISO8601标准时间 + return ISO8601DateParser.INSTANCE.parse(dateStr); } //标准日期格式(包括单个数字的日期时间) @@ -853,10 +826,9 @@ public class DateUtil extends CalendarUtil { // 没有更多匹配的时间格式 throw new DateException("No format fit for date String [{}] !", dateStr); } - // ------------------------------------ Parse end ---------------------------------------------- - - // ------------------------------------ Offset start ---------------------------------------------- + // endregion + // region ----- offset /** * 修改日期为某个时间字段起始时间 * @@ -1096,7 +1068,6 @@ public class DateUtil extends CalendarUtil { public static DateTime endOfYear(final Date date) { return new DateTime(endOfYear(calendar(date))); } - // --------------------------------------------------- Offset for now /** * 昨天 @@ -1243,9 +1214,9 @@ public class DateUtil extends CalendarUtil { public static DateTime offset(final Date date, final DateField dateField, final int offset) { return dateNew(date).offset(dateField, offset); } + // endregion - // ------------------------------------ Offset end ---------------------------------------------- - + // region ----- between /** * 判断两个日期相差的时长,只保留绝对值 * @@ -1351,7 +1322,9 @@ public class DateUtil extends CalendarUtil { public static long betweenYear(final Date beginDate, final Date endDate, final boolean isReset) { return new DateBetween(beginDate, endDate).betweenYear(isReset); } + // endregion + // region ----- formatBetween /** * 格式化日期间隔输出 * @@ -1397,6 +1370,7 @@ public class DateUtil extends CalendarUtil { public static String formatBetween(final long betweenMs) { return new BetweenFormatter(betweenMs, BetweenFormatter.Level.MILLISECOND).format(); } + // endregion /** * 当前日期是否在日期指定范围内
      @@ -1992,7 +1966,7 @@ public class DateUtil extends CalendarUtil { * @return 单位简写名称 * @since 5.7.16 */ - public static String getShotName(final TimeUnit unit) { + public static String getShortName(final TimeUnit unit) { switch (unit) { case NANOSECONDS: return "ns"; diff --git a/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java b/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java index 6213e5b87..d748bf201 100755 --- a/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/StopWatch.java @@ -352,7 +352,7 @@ public class StopWatch { unit = TimeUnit.NANOSECONDS; } return StrUtil.format("StopWatch '{}': running time = {} {}", - this.id, getTotal(unit), DateUtil.getShotName(unit)); + this.id, getTotal(unit), DateUtil.getShortName(unit)); } /** @@ -382,7 +382,7 @@ public class StopWatch { sb.append("No task info kept"); } else { sb.append("---------------------------------------------").append(FileUtil.getLineSeparator()); - sb.append(DateUtil.getShotName(unit)).append(" % Task name").append(FileUtil.getLineSeparator()); + sb.append(DateUtil.getShortName(unit)).append(" % Task name").append(FileUtil.getLineSeparator()); sb.append("---------------------------------------------").append(FileUtil.getLineSeparator()); final NumberFormat nf = NumberFormat.getNumberInstance(); diff --git a/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java index b045f2838..1fd6b8e0e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/TimeUtil.java @@ -23,11 +23,6 @@ import java.util.function.Function; * @since 6.0.0 */ public class TimeUtil extends TemporalAccessorUtil { - /** - * UTC 的 ZoneID - */ - public static final ZoneId ZONE_ID_UTC = ZoneId.of("UTC"); - /** * 当前时间,默认时区 * @@ -53,7 +48,7 @@ public class TimeUtil extends TemporalAccessorUtil { * @return {@link LocalDateTime} */ public static LocalDateTime ofUTC(final Instant instant) { - return of(instant, ZONE_ID_UTC); + return of(instant, ZoneUtil.ZONE_ID_UTC); } /** @@ -222,7 +217,7 @@ public class TimeUtil extends TemporalAccessorUtil { /** * 解析日期时间字符串为{@link LocalDateTime},格式支持日期时间、日期、时间
      - * 如果formatter为{code null},则使用{@link DateTimeFormatter#ISO_LOCAL_DATE_TIME} + * 如果formatter为{@code null},则使用{@link DateTimeFormatter#ISO_LOCAL_DATE_TIME} * * @param text 日期时间字符串 * @param formatter 日期格式化器,预定义的格式见:{@link DateTimeFormatter} diff --git a/hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java b/hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java index 008e648cc..2f11af19a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/ZoneUtil.java @@ -11,6 +11,15 @@ import java.util.TimeZone; */ public class ZoneUtil { + /** + * UTC 的 ZoneID + */ + public static final TimeZone ZONE_UTC = TimeZone.getTimeZone("UTC"); + /** + * UTC 的 TimeZone + */ + public static final ZoneId ZONE_ID_UTC = ZONE_UTC.toZoneId(); + /** * {@link ZoneId}转换为{@link TimeZone},{@code null}则返回系统默认值 * diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/UTCDateParser.java b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/ISO8601DateParser.java similarity index 81% rename from hutool-core/src/main/java/cn/hutool/core/date/format/parser/UTCDateParser.java rename to hutool-core/src/main/java/cn/hutool/core/date/format/parser/ISO8601DateParser.java index ed295d133..77fc2cb0b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/format/parser/UTCDateParser.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/format/parser/ISO8601DateParser.java @@ -9,7 +9,7 @@ import cn.hutool.core.text.StrUtil; import cn.hutool.core.util.CharUtil; /** - * UTC日期字符串(JDK的Date对象toString默认格式)解析,支持格式; + * ISO8601日期字符串(JDK的Date对象toString默认格式)解析,支持格式; *
        *
      1. yyyy-MM-dd'T'HH:mm:ss'Z'
      2. *
      3. yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
      4. @@ -22,13 +22,13 @@ import cn.hutool.core.util.CharUtil; * @author looly * @since 6.0.0 */ -public class UTCDateParser extends DefaultDateBasic implements DateParser { +public class ISO8601DateParser extends DefaultDateBasic implements DateParser { private static final long serialVersionUID = 1L; /** * 单例对象 */ - public static UTCDateParser INSTANCE = new UTCDateParser(); + public static ISO8601DateParser INSTANCE = new ISO8601DateParser(); @Override public DateTime parse(String source) { @@ -61,10 +61,10 @@ public class UTCDateParser extends DefaultDateBasic implements DateParser { if (StrUtil.contains(source, CharUtil.DOT)) { // 带毫秒,格式类似:2018-09-13T05:34:31.999+08:00 source = normalizeMillSeconds(source, ".", "+"); - return new DateTime(source, DatePattern.UTC_MS_WITH_XXX_OFFSET_FORMAT); + return new DateTime(source, DatePattern.ISO8601_MS_WITH_XXX_OFFSET_FORMAT); } else { // 格式类似:2018-09-13T05:34:31+08:00 - return new DateTime(source, DatePattern.UTC_WITH_XXX_OFFSET_FORMAT); + return new DateTime(source, DatePattern.ISO8601_WITH_XXX_OFFSET_FORMAT); } } else if(ReUtil.contains("-\\d{2}:?00", source)){ // Issue#2612,类似 2022-09-14T23:59:00-08:00 或者 2022-09-14T23:59:00-0800 @@ -78,22 +78,22 @@ public class UTCDateParser extends DefaultDateBasic implements DateParser { if (StrUtil.contains(source, CharUtil.DOT)) { // 带毫秒,格式类似:2018-09-13T05:34:31.999-08:00 source = normalizeMillSeconds(source, ".", "-"); - return new DateTime(source, DatePattern.UTC_MS_WITH_XXX_OFFSET_FORMAT); + return new DateTime(source, DatePattern.ISO8601_MS_WITH_XXX_OFFSET_FORMAT); } else { // 格式类似:2018-09-13T05:34:31-08:00 - return new DateTime(source, DatePattern.UTC_WITH_XXX_OFFSET_FORMAT); + return new DateTime(source, DatePattern.ISO8601_WITH_XXX_OFFSET_FORMAT); } } else { - if (length == DatePattern.UTC_SIMPLE_PATTERN.length() - 2) { + if (length == DatePattern.ISO8601_PATTERN.length() - 2) { // 格式类似:2018-09-13T05:34:31 - return new DateTime(source, DatePattern.UTC_SIMPLE_FORMAT); - } else if (length == DatePattern.UTC_SIMPLE_PATTERN.length() - 5) { + return new DateTime(source, DatePattern.ISO8601_FORMAT); + } else if (length == DatePattern.ISO8601_PATTERN.length() - 5) { // 格式类似:2018-09-13T05:34 - return new DateTime(source + ":00", DatePattern.UTC_SIMPLE_FORMAT); + return new DateTime(source + ":00", DatePattern.ISO8601_FORMAT); } else if (StrUtil.contains(source, CharUtil.DOT)) { // 可能为: 2021-03-17T06:31:33.99 source = normalizeMillSeconds(source, ".", null); - return new DateTime(source, DatePattern.UTC_SIMPLE_MS_FORMAT); + return new DateTime(source, DatePattern.ISO8601_MS_FORMAT); } } // 没有更多匹配的时间格式 diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java b/hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java index 1e69e28a2..02c8b24fc 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/DefaultSegment.java @@ -1,5 +1,7 @@ package cn.hutool.core.lang; +import cn.hutool.core.text.StrUtil; + /** * 片段默认实现 * @@ -9,26 +11,32 @@ package cn.hutool.core.lang; */ public class DefaultSegment implements Segment { - protected T startIndex; + protected T beginIndex; protected T endIndex; /** * 构造 - * @param startIndex 起始位置 - * @param endIndex 结束位置 + * + * @param beginIndex 起始位置 + * @param endIndex 结束位置 */ - public DefaultSegment(final T startIndex, final T endIndex) { - this.startIndex = startIndex; + public DefaultSegment(final T beginIndex, final T endIndex) { + this.beginIndex = beginIndex; this.endIndex = endIndex; } @Override - public T getStartIndex() { - return this.startIndex; + public T getBeginIndex() { + return this.beginIndex; } @Override public T getEndIndex() { return this.endIndex; } + + @Override + public String toString() { + return StrUtil.format("[{}, {}]", beginIndex, endIndex); + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Segment.java b/hutool-core/src/main/java/cn/hutool/core/lang/Segment.java index 92ff0e385..be9a4dbaf 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Segment.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Segment.java @@ -19,7 +19,7 @@ public interface Segment { * * @return 起始位置 */ - T getStartIndex(); + T getBeginIndex(); /** * 获取结束位置 @@ -34,7 +34,7 @@ public interface Segment { * @return 片段长度 */ default T length(){ - final T start = Assert.notNull(getStartIndex(), "Start index must be not null!"); + final T start = Assert.notNull(getBeginIndex(), "Start index must be not null!"); final T end = Assert.notNull(getEndIndex(), "End index must be not null!"); return Convert.convert((Type) start.getClass(), NumberUtil.sub(end, start).abs()); } diff --git a/hutool-core/src/main/java/cn/hutool/core/map/LinkedForestMap.java b/hutool-core/src/main/java/cn/hutool/core/map/LinkedForestMap.java index a0b9fc00e..dbcf2fa18 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/LinkedForestMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/LinkedForestMap.java @@ -538,7 +538,7 @@ public class LinkedForestMap implements ForestMap { * 从当前节点开始,按广度优先向下遍历当前节点的所有子节点 * * @param includeCurrent 是否包含当前节点 - * @param consumer 对节点与节点和当前节点的距离的操作,当{code includeCurrent}为false时下标从1开始,否则从0开始 + * @param consumer 对节点与节点和当前节点的距离的操作,当{@code includeCurrent}为false时下标从1开始,否则从0开始 * @param breakTraverse 是否终止遍历,为null时默认总是返回{@code true} * @return 遍历到的最后一个节点 */ diff --git a/hutool-core/src/main/java/cn/hutool/core/math/NavigatePageInfo.java b/hutool-core/src/main/java/cn/hutool/core/math/NavigatePageInfo.java new file mode 100644 index 000000000..cee14bc65 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/math/NavigatePageInfo.java @@ -0,0 +1,71 @@ +package cn.hutool.core.math; + +public class NavigatePageInfo extends PageInfo{ + + private final int navigatePages = 8; //导航页码数 + private int[] navigatePageNumbers; //所有导航页号 + + public NavigatePageInfo(final int total, final int pageSize) { + super(total, pageSize); + + //基本参数设定之后进行导航页面的计算 + calcNavigatePageNumbers(); + } + + /** + * 得到所有导航页号 + * + * @return {int[]} + */ + public int[] getNavigatePageNumbers() { + return navigatePageNumbers; + } + + public String toString() { + final StringBuilder str = new StringBuilder(super.toString()); + str.append(", {navigatePageNumbers="); + final int len = navigatePageNumbers.length; + if (len > 0) str.append(navigatePageNumbers[0]); + for (int i = 1; i < len; i++) { + str.append(" ").append(navigatePageNumbers[i]); + } + str.append("}"); + return str.toString(); + } + + /** + * 计算导航页 + */ + private void calcNavigatePageNumbers() { + //当总页数小于或等于导航页码数时 + if (pages <= navigatePages) { + navigatePageNumbers = new int[pages]; + for (int i = 0; i < pages; i++) { + navigatePageNumbers[i] = i + 1; + } + } else { //当总页数大于导航页码数时 + navigatePageNumbers = new int[navigatePages]; + int startNum = pageNo - navigatePages / 2; + int endNum = pageNo + navigatePages / 2; + + if (startNum < 1) { + startNum = 1; + //(最前navPageCount页 + for (int i = 0; i < navigatePages; i++) { + navigatePageNumbers[i] = startNum++; + } + } else if (endNum > pages) { + endNum = pages; + //最后navPageCount页 + for (int i = navigatePages - 1; i >= 0; i--) { + navigatePageNumbers[i] = endNum--; + } + } else { + //所有中间页 + for (int i = 0; i < navigatePages; i++) { + navigatePageNumbers[i] = startNum++; + } + } + } + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/math/PageInfo.java b/hutool-core/src/main/java/cn/hutool/core/math/PageInfo.java new file mode 100644 index 000000000..a84079fff --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/math/PageInfo.java @@ -0,0 +1,291 @@ +package cn.hutool.core.math; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.DefaultSegment; +import cn.hutool.core.lang.Segment; + +/** + * 分页信息,通过提供的总数、页码、每页记录数等信息,计算总页数等信息
        + * 来自:https://bbs.csdn.net/topics/360010907 + * + * @author 莫取网名 + */ +public class PageInfo { + + private static final int DEFAULT_PAGE_SIZE = 10; + + /** + * 创建{@code PageInfo},默认当前页是1 + * + * @param total 总记录数 + * @param pageSize 每页显示记录数 + * @return {@code PageInfo} + */ + public static PageInfo of(final int total, final int pageSize) { + return new PageInfo(total, pageSize); + } + + /** + * 总记录数 + */ + int total; + /** + * 每页显示记录数 + */ + int pageSize; + /** + * 总页数 + */ + int pages; + /** + * 首页标识 + */ + int firstPageNo = 1; + /** + * 当前页 + */ + int pageNo = firstPageNo; + + /** + * 构造 + * + * @param total 总记录数 + * @param pageSize 每页显示记录数 + */ + public PageInfo(final int total, final int pageSize) { + init(total, pageSize); + } + + /** + * 初始化 + * + * @param total 总记录数 + * @param pageSize 每页显示记录数 + */ + private void init(final int total, int pageSize) { + Assert.isTrue(total >= 0, "Total must >= 0"); + + //设置基本参数 + this.total = total; + + if (pageSize < 1) { + pageSize = DEFAULT_PAGE_SIZE; + } + this.pageSize = pageSize; + // 因为总条数除以页大小的最大余数是页大小数-1, + // 因此加一个最大余数,保证舍弃的余数与最大余数凑1.x,就是一旦有余数则+1页 + this.pages = (total + pageSize - 1) / pageSize; + } + + /** + * 得到记录总数 + * + * @return {int} + */ + public int getTotal() { + return total; + } + + /** + * 得到每页显示多少条记录 + * + * @return {int} + */ + public int getPageSize() { + return pageSize; + } + + /** + * 得到页面总数 + * + * @return {int} + */ + public int getPages() { + return pages; + } + + /** + * 得到当前页号 + * + * @return {int} + */ + public int getPageNo() { + return pageNo; + } + + + /** + * 是否首页 + * + * @return 是否首页 + */ + public boolean isFirstPage() { + return getPageIndexBase1() == 1; + } + + /** + * 是否尾页 + * + * @return 是否尾页 + */ + public boolean isLastPage() { + return getPageIndexBase1() == this.pages; + } + + /** + * 是否有前一页 + * + * @return 是否有前一页 + */ + public boolean hasPreviousPage() { + return getPageIndexBase1() > 1; + } + + /** + * 是否有下一页 + * + * @return 是否有下一页 + */ + public boolean hasNextPage() { + return getBeginIndex() < this.pages; + } + + /** + * 当前页是否可用(是否大于firstPageNum且小于总页数) + * @return 是否可用 + */ + public boolean isValidPage(){ + return this.getPageIndexBase1() <= this.pages; + } + + /** + * 获取当前页的开始记录index(包含) + * + * @return 开始记录index(包含) + */ + public int getBeginIndex() { + return (getPageIndexBase1() -1) * this.pageSize; + } + + /** + * 获取当前页的结束记录index(不包含) + *
          + *
        • 当开始index超出total时,返回正常值
        • + *
        • 当开始index未超出total但是计算的end超出时,返回total + 1
        • + *
        • 当开始index和end都未超出时,返回正常值
        • + *
        + * + * @return 结束记录index(不包含) + */ + public int getEndIndexExclude() { + return getEndIndex() + 1; + } + + /** + * 获取当前页的结束记录index(包含) + *
          + *
        • 当开始index超出total时,返回正常值
        • + *
        • 当开始index未超出total但是计算的end超出时,返回total
        • + *
        • 当开始index和end都未超出时,返回正常值
        • + *
        + * + * @return 结束记录index(包含) + */ + public int getEndIndex() { + final int begin = getBeginIndex(); + int end = begin + this.pageSize - 1; + if (begin <= this.total && end > this.total) { + end = this.total; + } + return end; + } + + /** + * 将页数和每页条目数转换为开始位置和结束位置
        + * 此方法用于包括结束位置的分页方法
        + * 例如: + *
        +	 * 页码:1,每页10 =》 [0, 9]
        +	 * 页码:2,每页10 =》 [10, 19]
        +	 * ……
        +	 * 
        + * + * @return {@link Segment} + */ + public Segment getSegment() { + return new DefaultSegment<>(getBeginIndex(), getEndIndex()); + } + + /** + * 获取设置首页编号,即以数字几为第一页标志 + * + * @return 设置首页编号 + */ + public int getFirstPageNo() { + return this.firstPageNo; + } + + /** + * 设置首页编号,即以数字几为第一页标志
        + * 如设置0,则0表示第一页,1表示第二页 + * + * @param firstPageNo 首页编号 + * @return this + */ + public PageInfo setFirstPageNo(final int firstPageNo) { + this.firstPageNo = firstPageNo; + return this; + } + + /** + * 设置当前页码 + * + * @param pageNo 当前页码 + * @return this + */ + public PageInfo setPageNo(final int pageNo) { + //根据输入可能错误的当前号码进行自动纠正 + // 不判断后边界,因为当页码超出边界,应该返回一个空区间的数据,而非始终保持在最后一页 + this.pageNo = Math.max(pageNo, firstPageNo); + return this; + } + + /** + * 下一页,即当前页码+1 + * + * @return this + */ + public PageInfo nextPage() { + return setPageNo(this.pageNo + 1); + } + + /** + * 上一页,即当前页码-1,直到第一页则始终为第一页 + * + * @return this + */ + public PageInfo PreviousPage() { + return setPageNo(this.pageNo - 1); + } + + public String toString() { + return "{" + + "total=" + total + + ",pages=" + pages + + ",pageNumber=" + pageNo + + ",limit=" + pageSize + + ",isFirstPage=" + isFirstPage() + + ",isLastPage=" + isLastPage() + + ",hasPreviousPage=" + hasPreviousPage() + + ",hasNextPage=" + hasNextPage() + + "}"; + } + + /** + * 获取页码序号,第一个序号就是1 + * @return 页码序号 + */ + private int getPageIndexBase1(){ + return this.pageNo - this.firstPageNo + 1; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/reflect/MethodHandleUtil.java b/hutool-core/src/main/java/cn/hutool/core/reflect/MethodHandleUtil.java index 4034e9f42..6e7531bec 100644 --- a/hutool-core/src/main/java/cn/hutool/core/reflect/MethodHandleUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/reflect/MethodHandleUtil.java @@ -121,7 +121,6 @@ public class MethodHandleUtil { * return "Quack"; * } * } - *

        * Duck duck = (Duck) Proxy.newProxyInstance( * ClassLoaderUtil.getClassLoader(), * new Class[] { Duck.class }, @@ -167,7 +166,6 @@ public class MethodHandleUtil { * return "Quack"; * } * } - *

        * Duck duck = (Duck) Proxy.newProxyInstance( * ClassLoaderUtil.getClassLoader(), * new Class[] { Duck.class }, @@ -193,7 +191,6 @@ public class MethodHandleUtil { * return "Quack"; * } * } - *

        * Duck duck = (Duck) Proxy.newProxyInstance( * ClassLoaderUtil.getClassLoader(), * new Class[] { Duck.class }, diff --git a/hutool-core/src/main/java/cn/hutool/core/reflect/ParameterizedTypeImpl.java b/hutool-core/src/main/java/cn/hutool/core/reflect/ParameterizedTypeImpl.java index e19891095..d0777c541 100644 --- a/hutool-core/src/main/java/cn/hutool/core/reflect/ParameterizedTypeImpl.java +++ b/hutool-core/src/main/java/cn/hutool/core/reflect/ParameterizedTypeImpl.java @@ -1,12 +1,12 @@ package cn.hutool.core.reflect; +import cn.hutool.core.text.StrUtil; +import cn.hutool.core.util.ArrayUtil; + import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.text.StrUtil; - /** * {@link ParameterizedType} 接口实现,用于重新定义泛型类型 * @@ -70,13 +70,14 @@ public class ParameterizedTypeImpl implements ParameterizedType, Serializable { } /** - * 追加 {@code types} 到 @{code buf},使用 {@code sep} 分隔 + * 追加 {@code types} 到 {@code buf},使用 {@code sep} 分隔 * * @param buf 目标 * @param sep 分隔符 * @param types 加入的类型 * @return {@code buf} */ + @SuppressWarnings("SameParameterValue") private static StringBuilder appendAllTo(final StringBuilder buf, final String sep, final Type... types) { if (ArrayUtil.isNotEmpty(types)) { boolean isFirst = true; diff --git a/hutool-core/src/main/java/cn/hutool/core/regex/ReUtil.java b/hutool-core/src/main/java/cn/hutool/core/regex/ReUtil.java index 3c015c1a9..897e118fa 100755 --- a/hutool-core/src/main/java/cn/hutool/core/regex/ReUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/regex/ReUtil.java @@ -197,7 +197,7 @@ public class ReUtil { * @return 匹配后得到的字符串数组,按照分组顺序依次列出,未匹配到返回空列表,任何一个参数为null返回null * @since 4.0.13 */ - public static List getAllGroups(Pattern pattern, CharSequence content, boolean withGroup0) { + public static List getAllGroups(final Pattern pattern, final CharSequence content, final boolean withGroup0) { return getAllGroups(pattern, content, withGroup0, false); } @@ -211,12 +211,12 @@ public class ReUtil { * @return 匹配后得到的字符串数组,按照分组顺序依次列出,未匹配到返回空列表,任何一个参数为null返回null * @since 4.0.13 */ - public static List getAllGroups(Pattern pattern, CharSequence content, boolean withGroup0, boolean findAll) { + public static List getAllGroups(final Pattern pattern, final CharSequence content, final boolean withGroup0, final boolean findAll) { if (null == content || null == pattern) { return null; } - ArrayList result = new ArrayList<>(); + final ArrayList result = new ArrayList<>(); final Matcher matcher = pattern.matcher(content); while (matcher.find()) { final int startGroup = withGroup0 ? 0 : 1; @@ -893,7 +893,6 @@ public class ReUtil { /** * 替换所有正则匹配的文本,并使用自定义函数决定如何替换
        * replaceFun可以通过{@link Matcher}提取出匹配到的内容的不同部分,然后经过重新处理、组装变成新的内容放回原位。 - *

        *

         	 *     replaceAll(this.content, "(\\d+)", parameters -> "-" + parameters.group(1) + "-")
         	 *     // 结果为:"ZZZaaabbbccc中文-1234-"
        @@ -912,7 +911,6 @@ public class ReUtil {
         	/**
         	 * 替换所有正则匹配的文本,并使用自定义函数决定如何替换
        * replaceFun可以通过{@link Matcher}提取出匹配到的内容的不同部分,然后经过重新处理、组装变成新的内容放回原位。 - *

        *

         	 *     replaceAll(this.content, "(\\d+)", parameters -> "-" + parameters.group(1) + "-")
         	 *     // 结果为:"ZZZaaabbbccc中文-1234-"
        diff --git a/hutool-core/src/main/java/cn/hutool/core/text/dfa/NFA.java b/hutool-core/src/main/java/cn/hutool/core/text/dfa/NFA.java
        new file mode 100644
        index 000000000..468658a62
        --- /dev/null
        +++ b/hutool-core/src/main/java/cn/hutool/core/text/dfa/NFA.java
        @@ -0,0 +1,145 @@
        +package cn.hutool.core.text.dfa;
        +
        +import java.util.*;
        +
        +/**
        + * 

        + * + * 基于非确定性有穷自动机(NFA) 实现的多模匹配工具 + * + * @author renyp + */ +public class NFA { + private final Node root; + + /** + * 默认构造 + */ + public NFA() { + this.root = new Node(); + } + + /** + * 构造函数 并 初始化词库 + * + * @param words 添加的新词 + */ + public NFA(final String... words) { + this(); + this.insert(words); + } + + /** + * 词库添加新词,初始化查找树 + * + * @param word 添加的新词 + */ + public void insert(final String word) { + Node p = root; + for (final char curr : word.toCharArray()) { + if (p.next.get((int) curr) == null) { + p.next.put((int) curr, new Node()); + } + p = p.next.get((int) curr); + } + p.flag = true; + p.str = word; + } + + /** + * 词库批量添加新词,初始化查找树 + * + * @param words 添加的新词 + */ + public void insert(final String... words) { + for (final String word : words) { + this.insert(word); + } + } + + /** + * 构建基于NFA模型的 AC自动机 + */ + public void buildAc() { + final Queue queue = new LinkedList<>(); + final Node p = root; + for (final Integer key : p.next.keySet()) { + p.next.get(key).fail = root; + queue.offer(p.next.get(key)); + } + while (!queue.isEmpty()) { + final Node curr = queue.poll(); + for (final Integer key : curr.next.keySet()) { + Node fail = curr.fail; + // 查找当前节点匹配失败,他对应等效匹配的节点是哪个 + while (fail != null && fail.next.get(key) == null) { + fail = fail.fail; + } + // 代码到这,有两种可能,fail不为null,说明找到了fail;fail为null,没有找到,那么就把fail指向root节点(当到该节点匹配失败,那么从root节点开始重新匹配) + if (fail != null) { + fail = fail.next.get(key); + } else { + fail = root; + } + curr.next.get(key).fail = fail; + queue.offer(curr.next.get(key)); + } + } + } + + /** + * @param text 查询的文本(母串) + * @return 关键字列表 + */ + public List find(final String text) { + return this.find(text, true); + } + + /** + * @param text 查找的文本(母串) + * @param isDensityMatch 是否密集匹配 + * @return 关键字列表 + */ + public List find(final String text, final boolean isDensityMatch) { + final List ans = new ArrayList<>(); + Node p = root, k; + for (int i = 0, len = text.length(); i < len; i++) { + final int ind = text.charAt(i); + // 状态转移(沿着fail指针链接的链表,此处区别于DFA模型) + while (p != null && p.next.get(ind) == null) { + p = p.fail; + } + if (p == null) { + p = root; + } else { + p = p.next.get(ind); + } + // 提取结果(沿着fail指针链接的链表,此处区别于DFA模型) + k = p; + while (k != null) { + if (k.flag) { + ans.add(new FoundWord(k.str, k.str, i - k.str.length() + 1, i)); + if (!isDensityMatch) { + p = root; + break; + } + } + k = k.fail; + } + } + return ans; + } + + private static class Node { + + boolean flag; + Node fail; + String str; + Map next; + + public Node() { + this.flag = false; + next = new HashMap<>(); + } + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/text/dfa/SensitiveUtil.java b/hutool-core/src/main/java/cn/hutool/core/text/dfa/SensitiveUtil.java index 07e1cb64e..ba03bb2a6 100755 --- a/hutool-core/src/main/java/cn/hutool/core/text/dfa/SensitiveUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/dfa/SensitiveUtil.java @@ -171,7 +171,7 @@ public final class SensitiveUtil { } : sensitiveProcessor; final Map foundWordMap = new HashMap<>(foundWordList.size(), 1); - foundWordList.forEach(foundWord -> foundWordMap.put(foundWord.getStartIndex(), foundWord)); + foundWordList.forEach(foundWord -> foundWordMap.put(foundWord.getBeginIndex(), foundWord)); final int length = text.length(); final StringBuilder textStringBuilder = new StringBuilder(); for (int i = 0; i < length; i++) { diff --git a/hutool-core/src/main/java/cn/hutool/core/text/split/SplitUtil.java b/hutool-core/src/main/java/cn/hutool/core/text/split/SplitUtil.java index e6505b214..0836700b4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/split/SplitUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/split/SplitUtil.java @@ -1,5 +1,6 @@ package cn.hutool.core.text.split; +import cn.hutool.core.collection.ListUtil; import cn.hutool.core.regex.PatternPool; import cn.hutool.core.text.StrUtil; import cn.hutool.core.text.finder.CharFinder; @@ -302,7 +303,8 @@ public class SplitUtil { /** * 切分字符串
        - * 如果为空字符串或者null 则返回空集合 + * 如果提供的字符串为{@code null},则返回一个空的{@link ArrayList}
        + * 如果提供的字符串为"",则当ignoreEmpty时返回空的{@link ArrayList},否则返回只有一个""元素的{@link ArrayList} * * @param text 被切分的字符串 * @param separator 分隔符字符串 @@ -314,8 +316,10 @@ public class SplitUtil { * @since 3.2.1 */ public static List split(final CharSequence text, final String separator, final int limit, final boolean isTrim, final boolean ignoreEmpty, final boolean ignoreCase) { - if (StrUtil.isEmpty(text)) { + if(null == text){ return new ArrayList<>(0); + } else if (0 == text.length()) { + return ignoreEmpty ? new ArrayList<>(0) : ListUtil.of(StrUtil.EMPTY); } final SplitIter splitIter = new SplitIter(text, new StrFinder(separator, ignoreCase), limit, ignoreEmpty); return splitIter.toList(isTrim); diff --git a/hutool-core/src/main/java/cn/hutool/core/thread/ThreadUtil.java b/hutool-core/src/main/java/cn/hutool/core/thread/ThreadUtil.java index de79b82eb..17d126d08 100644 --- a/hutool-core/src/main/java/cn/hutool/core/thread/ThreadUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/thread/ThreadUtil.java @@ -140,7 +140,7 @@ public class ThreadUtil { *

         	 *     1. 核心线程数与最大线程数为nThreads指定的大小
         	 *     2. 默认使用LinkedBlockingQueue,默认队列大小为1024
        -	 *     3. 如果isBlocked为{code true},当执行拒绝策略的时候会处于阻塞状态,直到能添加到队列中或者被{@link Thread#interrupt()}中断
        +	 *     3. 如果isBlocked为{@code true},当执行拒绝策略的时候会处于阻塞状态,直到能添加到队列中或者被{@link Thread#interrupt()}中断
         	 * 
        * * @param nThreads 线程池大小 @@ -159,7 +159,7 @@ public class ThreadUtil { *
         	 *     1. 核心线程数与最大线程数为nThreads指定的大小
         	 *     2. 默认使用LinkedBlockingQueue
        -	 *     3. 如果isBlocked为{code true},当执行拒绝策略的时候会处于阻塞状态,直到能添加到队列中或者被{@link Thread#interrupt()}中断
        +	 *     3. 如果isBlocked为{@code true},当执行拒绝策略的时候会处于阻塞状态,直到能添加到队列中或者被{@link Thread#interrupt()}中断
         	 * 
        * * @param nThreads 线程池大小 diff --git a/hutool-core/src/main/java/cn/hutool/core/util/JNDIUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/JNDIUtil.java index aca69c6cc..a1b9175ed 100755 --- a/hutool-core/src/main/java/cn/hutool/core/util/JNDIUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/JNDIUtil.java @@ -28,7 +28,7 @@ public class JNDIUtil { /** * 创建{@link InitialDirContext} * - * @param environment 环境参数,{code null}表示无参数 + * @param environment 环境参数,{@code null}表示无参数 * @return {@link InitialDirContext} */ public static InitialDirContext createInitialDirContext(final Map environment) { @@ -45,7 +45,7 @@ public class JNDIUtil { /** * 创建{@link InitialContext} * - * @param environment 环境参数,{code null}表示无参数 + * @param environment 环境参数,{@code null}表示无参数 * @return {@link InitialContext} */ public static InitialContext createInitialContext(final Map environment) { diff --git a/hutool-core/src/main/java/cn/hutool/core/util/PageUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/PageUtil.java deleted file mode 100644 index 3c49f1d5e..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/util/PageUtil.java +++ /dev/null @@ -1,273 +0,0 @@ -package cn.hutool.core.util; - -import cn.hutool.core.lang.DefaultSegment; -import cn.hutool.core.lang.Segment; - -/** - * 分页工具类 - * - * @author xiaoleilu - */ -public class PageUtil { - - private static int firstPageNo = 0; - - /** - * 获得首页的页码,可以为0或者1 - * - * @return 首页页码 - */ - public static int getFirstPageNo() { - return firstPageNo; - } - - /** - * 设置首页页码,可以为0或者1 - * - *
        -	 *     当设置为0时,页码0表示第一页,开始位置为0
        -	 *     当设置为1时,页码1表示第一页,开始位置为0
        -	 * 
        - * - * @param customFirstPageNo 自定义的首页页码,为0或者1 - */ - synchronized public static void setFirstPageNo(final int customFirstPageNo) { - firstPageNo = customFirstPageNo; - } - - /** - * 设置首页页码为1 - * - *
        -	 *     当设置为1时,页码1表示第一页,开始位置为0
        -	 * 
        - */ - public static void setOneAsFirstPageNo() { - setFirstPageNo(1); - } - - /** - * 将页数和每页条目数转换为开始位置
        - * 此方法用于不包括结束位置的分页方法
        - * 例如: - * - *
        -	 * 页码:0,每页10 =》 0
        -	 * 页码:1,每页10 =》 10
        -	 * ……
        -	 * 
        - * - *

        - * 当{@link #setFirstPageNo(int)}设置为1时: - *

        -	 * 页码:1,每页10 =》 0
        -	 * 页码:2,每页10 =》 10
        -	 * ……
        -	 * 
        - * - * @param pageNo 页码(从0计数) - * @param pageSize 每页条目数 - * @return 开始位置 - */ - public static int getStart(int pageNo, int pageSize) { - if (pageNo < firstPageNo) { - pageNo = firstPageNo; - } - - if (pageSize < 1) { - pageSize = 0; - } - - return (pageNo - firstPageNo) * pageSize; - } - - /** - * 将页数和每页条目数转换为结束位置
        - * 此方法用于不包括结束位置的分页方法
        - * 例如: - * - *
        -	 * 页码:0,每页10 =》 9
        -	 * 页码:1,每页10 =》 19
        -	 * ……
        -	 * 
        - * - *

        - * 当{@link #setFirstPageNo(int)}设置为1时: - *

        -	 * 页码:1,每页10 =》 9
        -	 * 页码:2,每页10 =》 19
        -	 * ……
        -	 * 
        - * - * @param pageNo 页码(从0计数) - * @param pageSize 每页条目数 - * @return 开始位置 - * @since 5.2.5 - */ - public static int getEnd(final int pageNo, final int pageSize) { - final int start = getStart(pageNo, pageSize); - return getEndByStart(start, pageSize); - } - - /** - * 将页数和每页条目数转换为开始位置和结束位置
        - * 此方法用于包括结束位置的分页方法
        - * 例如: - * - *
        -	 * 页码:0,每页10 =》 [0, 10]
        -	 * 页码:1,每页10 =》 [10, 20]
        -	 * ……
        -	 * 
        - * - *

        - * 当{@link #setFirstPageNo(int)}设置为1时: - *

        -	 * 页码:1,每页10 =》 [0, 10]
        -	 * 页码:2,每页10 =》 [10, 20]
        -	 * ……
        -	 * 
        - * - * @param pageNo 页码(从0计数) - * @param pageSize 每页条目数 - * @return 第一个数为开始位置,第二个数为结束位置 - */ - public static int[] transToStartEnd(final int pageNo, final int pageSize) { - final int start = getStart(pageNo, pageSize); - return new int[]{start, getEndByStart(start, pageSize)}; - } - - /** - * 将页数和每页条目数转换为开始位置和结束位置
        - * 此方法用于包括结束位置的分页方法
        - * 例如: - * - *
        -	 * 页码:0,每页10 =》 [0, 10]
        -	 * 页码:1,每页10 =》 [10, 20]
        -	 * ……
        -	 * 
        - * - *

        - * 当{@link #setFirstPageNo(int)}设置为1时: - *

        -	 * 页码:1,每页10 =》 [0, 10]
        -	 * 页码:2,每页10 =》 [10, 20]
        -	 * ……
        -	 * 
        - * - * @param pageNo 页码(从0计数) - * @param pageSize 每页条目数 - * @return {@link Segment} - * @since 5.5.3 - */ - public static Segment toSegment(final int pageNo, final int pageSize) { - final int[] startEnd = transToStartEnd(pageNo, pageSize); - return new DefaultSegment<>(startEnd[0], startEnd[1]); - } - - /** - * 根据总数计算总页数 - * - * @param totalCount 总数 - * @param pageSize 每页数 - * @return 总页数 - */ - public static int totalPage(final int totalCount, final int pageSize) { - return totalPage((long) totalCount, pageSize); - } - - /** - * 根据总数计算总页数 - * - * @param totalCount 总数 - * @param pageSize 每页数 - * @return 总页数 - * @since 5.8.5 - */ - public static int totalPage(final long totalCount, final int pageSize) { - if (pageSize == 0) { - return 0; - } - return Math.toIntExact(totalCount % pageSize == 0 - ? (totalCount / pageSize) : (totalCount / pageSize + 1)); - } - - /** - * 分页彩虹算法
        - * 来自:https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java
        - * 通过传入的信息,生成一个分页列表显示 - * - * @param pageNo 当前页 - * @param totalPage 总页数 - * @param displayCount 每屏展示的页数 - * @return 分页条 - */ - public static int[] rainbow(final int pageNo, final int totalPage, final int displayCount) { - // displayCount % 2 - final boolean isEven = (displayCount & 1) == 0; - final int left = displayCount >> 1; - int right = displayCount >> 1; - - int length = displayCount; - if (isEven) { - right++; - } - if (totalPage < displayCount) { - length = totalPage; - } - final int[] result = new int[length]; - if (totalPage >= displayCount) { - if (pageNo <= left) { - for (int i = 0; i < result.length; i++) { - result[i] = i + 1; - } - } else if (pageNo > totalPage - right) { - for (int i = 0; i < result.length; i++) { - result[i] = i + totalPage - displayCount + 1; - } - } else { - for (int i = 0; i < result.length; i++) { - result[i] = i + pageNo - left + (isEven ? 1 : 0); - } - } - } else { - for (int i = 0; i < result.length; i++) { - result[i] = i + 1; - } - } - return result; - - } - - /** - * 分页彩虹算法(默认展示10页)
        - * 来自:https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java - * - * @param currentPage 当前页 - * @param pageCount 总页数 - * @return 分页条 - */ - public static int[] rainbow(final int currentPage, final int pageCount) { - return rainbow(currentPage, pageCount, 10); - } - - //------------------------------------------------------------------------- Private method start - - /** - * 根据起始位置获取结束位置 - * - * @param start 起始位置 - * @param pageSize 每页条目数 - * @return 结束位置 - */ - private static int getEndByStart(final int start, int pageSize) { - if (pageSize < 1) { - pageSize = 0; - } - return start + pageSize; - } - - //------------------------------------------------------------------------- Private method end -} diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java index fc5f4a2b4..f87b0586e 100755 --- a/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java @@ -427,6 +427,26 @@ public class CollUtilTest { Assert.assertEquals("李四", groupByField.get(1).get(0).getName()); } + @Test + public void groupByFuncTest() { + final List list = ListUtil.of(new TestBean("张三", 12), new TestBean("李四", 13), new TestBean("王五", 12)); + final List> groupByField = CollUtil.groupByFunc(list, TestBean::getAge); + Assert.assertEquals("张三", groupByField.get(0).get(0).getName()); + Assert.assertEquals("王五", groupByField.get(0).get(1).getName()); + + Assert.assertEquals("李四", groupByField.get(1).get(0).getName()); + } + + @Test + public void groupByFunc2Test() { + final List list = ListUtil.of(new TestBean("张三", 12), new TestBean("李四", 13), new TestBean("王五", 12)); + final List> groupByField = CollUtil.groupByFunc(list, a -> a.getAge() > 12); + Assert.assertEquals("张三", groupByField.get(0).get(0).getName()); + Assert.assertEquals("王五", groupByField.get(0).get(1).getName()); + + Assert.assertEquals("李四", groupByField.get(1).get(0).getName()); + } + @Test public void sortByPropertyTest() { final List list = ListUtil.of( @@ -844,7 +864,7 @@ public class CollUtilTest { objects.add(Dict.of().set("name", "姓名:" + i)); } - Assert.assertEquals(0, CollUtil.page(3, 5, objects).size()); + Assert.assertEquals(0, ListUtil.page(objects, 3, 5).size()); } @Test @@ -854,7 +874,7 @@ public class CollUtilTest { final List result = CollUtil.subtractToList(list1, list2); Assert.assertEquals(1, result.size()); - Assert.assertEquals(1L, (long)result.get(0)); + Assert.assertEquals(1L, (long) result.get(0)); } @Test diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java index c8d330e88..e4e40d0ac 100644 --- a/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java @@ -2,7 +2,7 @@ package cn.hutool.core.collection; import cn.hutool.core.date.StopWatch; import cn.hutool.core.lang.Console; -import cn.hutool.core.util.PageUtil; +import cn.hutool.core.math.PageInfo; import cn.hutool.core.util.RandomUtil; import lombok.AllArgsConstructor; import lombok.Data; @@ -10,11 +10,7 @@ import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; public class ListUtilTest { @@ -105,51 +101,32 @@ public class ListUtilTest { } @Test - public void pageTest() { + public void pageTest1() { final List a = ListUtil.ofLinked(1, 2, 3, 4, 5); - PageUtil.setFirstPageNo(1); - final int[] a_1 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] a1 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] a2 = ListUtil.page(2, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] a3 = ListUtil.page(3, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] a4 = ListUtil.page(4, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] a_1 = ListUtil.page(a, 0, 2).stream().mapToInt(Integer::valueOf).toArray(); + final int[] a1 = ListUtil.page(a, 0, 2).stream().mapToInt(Integer::valueOf).toArray(); + final int[] a2 = ListUtil.page(a, 1, 2).stream().mapToInt(Integer::valueOf).toArray(); + final int[] a3 = ListUtil.page(a, 2, 2).stream().mapToInt(Integer::valueOf).toArray(); + final int[] a4 = ListUtil.page(a, 3, 2).stream().mapToInt(Integer::valueOf).toArray(); Assert.assertArrayEquals(new int[]{1, 2}, a_1); Assert.assertArrayEquals(new int[]{1, 2}, a1); Assert.assertArrayEquals(new int[]{3, 4}, a2); Assert.assertArrayEquals(new int[]{5}, a3); Assert.assertArrayEquals(new int[]{}, a4); + } - - PageUtil.setFirstPageNo(2); - final int[] b_1 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] b1 = ListUtil.page(2, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] b2 = ListUtil.page(3, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] b3 = ListUtil.page(4, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] b4 = ListUtil.page(5, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - Assert.assertArrayEquals(new int[]{1, 2}, b_1); - Assert.assertArrayEquals(new int[]{1, 2}, b1); - Assert.assertArrayEquals(new int[]{3, 4}, b2); - Assert.assertArrayEquals(new int[]{5}, b3); - Assert.assertArrayEquals(new int[]{}, b4); - - PageUtil.setFirstPageNo(0); - final int[] c_1 = ListUtil.page(-1, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] c1 = ListUtil.page(0, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] c2 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] c3 = ListUtil.page(2, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] c4 = ListUtil.page(3, 2, a).stream().mapToInt(Integer::valueOf).toArray(); - Assert.assertArrayEquals(new int[]{1, 2}, c_1); - Assert.assertArrayEquals(new int[]{1, 2}, c1); - Assert.assertArrayEquals(new int[]{3, 4}, c2); - Assert.assertArrayEquals(new int[]{5}, c3); - Assert.assertArrayEquals(new int[]{}, c4); - - - PageUtil.setFirstPageNo(1); - final int[] d1 = ListUtil.page(0, 8, a).stream().mapToInt(Integer::valueOf).toArray(); + @Test + public void pageTest2() { + final List a = ListUtil.ofLinked(1, 2, 3, 4, 5); + final int[] d1 = ListUtil.page(a, PageInfo.of(a.size(), 8).setFirstPageNo(0).setPageNo(0)) + .stream().mapToInt(Integer::valueOf).toArray(); Assert.assertArrayEquals(new int[]{1, 2, 3, 4, 5}, d1); + } + @Test + public void pageTest3() { + final List a = ListUtil.ofLinked(1, 2, 3, 4, 5); // page with consumer final List> pageListData = new ArrayList<>(); ListUtil.page(a, 2, pageListData::add); @@ -168,9 +145,6 @@ public class ListUtilTest { Assert.assertArrayEquals(new int[]{}, pageListData.get(0).stream().mapToInt(Integer::valueOf).toArray()); Assert.assertArrayEquals(new int[]{3, 4}, pageListData.get(1).stream().mapToInt(Integer::valueOf).toArray()); Assert.assertArrayEquals(new int[]{5}, pageListData.get(2).stream().mapToInt(Integer::valueOf).toArray()); - - // 恢复默认值,避免影响其他测试用例 - PageUtil.setFirstPageNo(0); } @Test diff --git a/hutool-core/src/test/java/cn/hutool/core/date/DateTimeTest.java b/hutool-core/src/test/java/cn/hutool/core/date/DateTimeTest.java index 8729ec3ec..4a9122342 100644 --- a/hutool-core/src/test/java/cn/hutool/core/date/DateTimeTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/date/DateTimeTest.java @@ -109,10 +109,10 @@ public class DateTimeTest { public void toStringTest2() { final DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT); - String dateStr = dateTime.toString(DatePattern.UTC_WITH_ZONE_OFFSET_PATTERN); + String dateStr = dateTime.toString(DatePattern.ISO8601_WITH_ZONE_OFFSET_PATTERN); Assert.assertEquals("2017-01-05T12:34:23+0800", dateStr); - dateStr = dateTime.toString(DatePattern.UTC_WITH_XXX_OFFSET_PATTERN); + dateStr = dateTime.toString(DatePattern.ISO8601_WITH_XXX_OFFSET_PATTERN); Assert.assertEquals("2017-01-05T12:34:23+08:00", dateStr); } diff --git a/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java index 9d2bafeff..167c59c8e 100755 --- a/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java @@ -1,6 +1,5 @@ package cn.hutool.core.date; -import cn.hutool.core.collection.ListUtil; import cn.hutool.core.date.BetweenFormatter.Level; import cn.hutool.core.date.format.FastDateFormat; import cn.hutool.core.lang.Console; @@ -14,15 +13,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.TimeZone; +import java.util.*; /** * 时间工具单元测试
        @@ -796,23 +787,6 @@ public class DateUtilTest { Assert.assertEquals(0, DateUtil.compare(date11, date22, DatePattern.NORM_MONTH_PATTERN)); } - @Test - public void yearAndQTest() { - final String yearAndQuarter = DateUtil.yearAndQuarter(DateUtil.parse("2018-12-01")); - Assert.assertEquals("20184", yearAndQuarter); - - final LinkedHashSet yearAndQuarters = DateUtil.yearAndQuarter(DateUtil.parse("2018-09-10"), DateUtil.parse("2018-12-20")); - final List list = ListUtil.of(false, yearAndQuarters); - Assert.assertEquals(2, list.size()); - Assert.assertEquals("20183", list.get(0)); - Assert.assertEquals("20184", list.get(1)); - - final LinkedHashSet yearAndQuarters2 = DateUtil.yearAndQuarter(DateUtil.parse("2018-10-10"), DateUtil.parse("2018-12-10")); - final List list2 = ListUtil.of(false, yearAndQuarters2); - Assert.assertEquals(1, list2.size()); - Assert.assertEquals("20184", list2.get(0)); - } - @Test public void formatHttpDateTest() { final String formatHttpDate = DateUtil.formatHttpDate(DateUtil.parse("2019-01-02 22:32:01")); diff --git a/hutool-core/src/test/java/cn/hutool/core/date/Issue2981Test.java b/hutool-core/src/test/java/cn/hutool/core/date/Issue2981Test.java new file mode 100755 index 000000000..432459ff0 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/date/Issue2981Test.java @@ -0,0 +1,22 @@ +package cn.hutool.core.date; + +import org.junit.Assert; +import org.junit.Test; + +public class Issue2981Test { + /** + * https://github.com/dromara/hutool/issues/2981
        + * 按照ISO8601规范,以Z结尾表示UTC时间,否则为当地时间 + */ + @SuppressWarnings("DataFlowIssue") + @Test + public void parseUTCTest() { + final String str1 = "2019-01-01T00:00:00.000Z"; + final String str2 = "2019-01-01T00:00:00.000"; + final String str3 = "2019-01-01 00:00:00.000"; + + Assert.assertEquals(1546300800000L, DateUtil.parse(str1).getTime()); + Assert.assertEquals(1546272000000L, DateUtil.parse(str2).getTime()); + Assert.assertEquals(1546272000000L, DateUtil.parse(str3).getTime()); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/date/TimeUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/date/TimeUtilTest.java index 75f8f8aa1..3ff41ae9f 100644 --- a/hutool-core/src/test/java/cn/hutool/core/date/TimeUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/date/TimeUtilTest.java @@ -27,12 +27,22 @@ public class TimeUtilTest { final String dateStr = "2020-01-23T12:23:56"; final DateTime dt = DateUtil.parse(dateStr); - LocalDateTime of = TimeUtil.of(dt); + final LocalDateTime of = TimeUtil.of(dt); Assert.assertNotNull(of); Assert.assertEquals(dateStr, of.toString()); + } - of = TimeUtil.ofUTC(dt.getTime()); - Assert.assertEquals(dateStr, of.toString()); + @SuppressWarnings("DataFlowIssue") + @Test + public void ofUTCTest() { + final String dateStr = "2020-01-23T12:23:56Z"; + final DateTime dt = DateUtil.parse(dateStr); + + final LocalDateTime of = TimeUtil.of(dt); + final LocalDateTime of2 = TimeUtil.ofUTC(dt.getTime()); + Assert.assertNotNull(of); + Assert.assertNotNull(of2); + Assert.assertEquals(of, of2); } @Test diff --git a/hutool-core/src/test/java/cn/hutool/core/math/PageInfoTest.java b/hutool-core/src/test/java/cn/hutool/core/math/PageInfoTest.java new file mode 100644 index 000000000..a5778a98f --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/math/PageInfoTest.java @@ -0,0 +1,32 @@ +package cn.hutool.core.math; + +import org.junit.Assert; +import org.junit.Test; + +public class PageInfoTest { + @Test + public void pagesTest() { + PageInfo pageInfo = new PageInfo(20, 3); + Assert.assertEquals(7, pageInfo.getPages()); + + pageInfo = new PageInfo(20, 4); + Assert.assertEquals(5, pageInfo.getPages()); + } + + @Test + public void getSegmentTest() { + final PageInfo page = PageInfo.of(20, 10); + Assert.assertEquals("[0, 9]", page.getSegment().toString()); + Assert.assertEquals("[10, 19]", page.nextPage().getSegment().toString()); + Assert.assertEquals("[20, 20]", page.nextPage().getSegment().toString()); + } + + @Test + public void getSegmentTest2() { + final PageInfo page = PageInfo.of(20, 10); + page.setFirstPageNo(0).setPageNo(0); + Assert.assertEquals("[0, 9]", page.getSegment().toString()); + Assert.assertEquals("[10, 19]", page.nextPage().getSegment().toString()); + Assert.assertEquals("[20, 20]", page.nextPage().getSegment().toString()); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/text/dfa/DfaTest.java b/hutool-core/src/test/java/cn/hutool/core/text/dfa/DfaTest.java index 3847800db..739f02fd0 100755 --- a/hutool-core/src/test/java/cn/hutool/core/text/dfa/DfaTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/dfa/DfaTest.java @@ -91,15 +91,15 @@ public class DfaTest { Assert.assertEquals(3, result.size()); Assert.assertEquals("赵", result.get(0).getWord()); - Assert.assertEquals(0, result.get(0).getStartIndex().intValue()); + Assert.assertEquals(0, result.get(0).getBeginIndex().intValue()); Assert.assertEquals(0, result.get(0).getEndIndex().intValue()); Assert.assertEquals("赵阿", result.get(1).getWord()); - Assert.assertEquals(0, result.get(1).getStartIndex().intValue()); + Assert.assertEquals(0, result.get(1).getBeginIndex().intValue()); Assert.assertEquals(1, result.get(1).getEndIndex().intValue()); Assert.assertEquals("赵阿三", result.get(2).getWord()); - Assert.assertEquals(0, result.get(2).getStartIndex().intValue()); + Assert.assertEquals(0, result.get(2).getBeginIndex().intValue()); Assert.assertEquals(2, result.get(2).getEndIndex().intValue()); } diff --git a/hutool-core/src/test/java/cn/hutool/core/text/dfa/NFATest.java b/hutool-core/src/test/java/cn/hutool/core/text/dfa/NFATest.java new file mode 100644 index 000000000..e38be87f3 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/text/dfa/NFATest.java @@ -0,0 +1,228 @@ +package cn.hutool.core.text.dfa; + +import cn.hutool.core.date.StopWatch; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; +import java.util.stream.Collectors; + +public class NFATest { + + /** + * 密集匹配 测试查找结果,并与WordTree对比效率 + */ + @Test + public void testFind() { + final NFA NFA = new NFA(); + NFA.insert("say", "her", "he", "she", "shr"); + NFA.buildAc(); + + final WordTree wordTree = new WordTree(); + wordTree.addWords("say", "her", "he", "she", "shr"); + + final StopWatch stopWatch = new StopWatch(); + final String input = "sasherhsay"; + + stopWatch.start("automaton_char_find"); + final List ans1 = NFA.find(input); + stopWatch.stop(); + + Assert.assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(","))); + Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue()); + Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue()); + Assert.assertEquals(3, ans1.get(1).getBeginIndex().intValue()); + Assert.assertEquals(4, ans1.get(1).getEndIndex().intValue()); + Assert.assertEquals(3, ans1.get(2).getBeginIndex().intValue()); + Assert.assertEquals(5, ans1.get(2).getEndIndex().intValue()); + Assert.assertEquals(7, ans1.get(3).getBeginIndex().intValue()); + Assert.assertEquals(9, ans1.get(3).getEndIndex().intValue()); + + stopWatch.start("wordtree_char_find"); + final List ans2 = wordTree.matchAll(input, -1, true, true); + stopWatch.stop(); + Assert.assertEquals("she,he,her,say", String.join(",", ans2)); + + //Console.log(stopWatch.prettyPrint()); + } + + /** + * 非密集匹配 测试查找结果,并与WordTree对比效率 + */ + @Test + public void testFindNotDensity() { + final NFA NFA = new NFA(); + NFA.insert("say", "her", "he", "she", "shr"); + NFA.buildAc(); + + final WordTree wordTree = new WordTree(); + wordTree.addWords("say", "her", "he", "she", "shr"); + + final StopWatch stopWatch = new StopWatch(); + final String input = "sasherhsay"; + + stopWatch.start("automaton_char_find_not_density"); + final List ans1 = NFA.find(input, false); + stopWatch.stop(); + Assert.assertEquals("she,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(","))); + Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue()); + Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue()); + Assert.assertEquals(7, ans1.get(1).getBeginIndex().intValue()); + Assert.assertEquals(9, ans1.get(1).getEndIndex().intValue()); + + stopWatch.start("wordtree_char_find_not_density"); + final List ans2 = wordTree.matchAll(input, -1, false, true); + stopWatch.stop(); + Assert.assertEquals("she,say", String.join(",", ans2)); + + //Console.log(stopWatch.prettyPrint()); + } + + /** + * 密集匹配 测试建树和查找,并与WordTree对比效率 + */ + @Test + public void testBuildAndFind() { + final StopWatch stopWatch = new StopWatch(); + final String input = "sasherhsay"; + + stopWatch.start("automaton_char_buid_find"); + final NFA NFALocal = new NFA(); + NFALocal.insert("say", "her", "he", "she", "shr"); + NFALocal.buildAc(); + final List ans1 = NFALocal.find(input); + stopWatch.stop(); + + Assert.assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(","))); + Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue()); + Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue()); + Assert.assertEquals(3, ans1.get(1).getBeginIndex().intValue()); + Assert.assertEquals(4, ans1.get(1).getEndIndex().intValue()); + Assert.assertEquals(3, ans1.get(2).getBeginIndex().intValue()); + Assert.assertEquals(5, ans1.get(2).getEndIndex().intValue()); + Assert.assertEquals(7, ans1.get(3).getBeginIndex().intValue()); + Assert.assertEquals(9, ans1.get(3).getEndIndex().intValue()); + + stopWatch.start("wordtree_char_build_find"); + final WordTree wordTreeLocal = new WordTree(); + wordTreeLocal.addWords("say", "her", "he", "she", "shr"); + final List ans2 = wordTreeLocal.matchAll(input, -1, true, true); + stopWatch.stop(); + Assert.assertEquals("she,he,her,say", String.join(",", ans2)); + + //Console.log(stopWatch.prettyPrint()); + } + + /** + * 密集匹配 构建树和查找 测试中文字符,并与wordTree对比效率 + */ + @Test + public void buildFindCnCharTest() { + final StopWatch stopWatch = new StopWatch(); + final String input = "赵啊三在做什么"; + + stopWatch.start("automaton_cn_build_find"); + final NFA NFALocal = new NFA(); + NFALocal.insert("赵", "赵啊", "赵啊三"); + NFALocal.buildAc(); + + final List result = NFALocal.find(input); + stopWatch.stop(); + + Assert.assertEquals(3, result.size()); + Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(","))); + Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex()); + Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex()); + Assert.assertEquals(Integer.valueOf(0), result.get(1).getBeginIndex()); + Assert.assertEquals(Integer.valueOf(1), result.get(1).getEndIndex()); + Assert.assertEquals(Integer.valueOf(0), result.get(2).getBeginIndex()); + Assert.assertEquals(Integer.valueOf(2), result.get(2).getEndIndex()); + + stopWatch.start("wordtree_cn_build_find"); + final WordTree wordTreeLocal = new WordTree(); + wordTreeLocal.addWords("赵", "赵啊", "赵啊三"); + + final List result1 = wordTreeLocal.matchAll(input, -1, true, true); + stopWatch.stop(); + + Assert.assertEquals(3, result1.size()); + Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1)); + + //Console.log(stopWatch.prettyPrint()); + } + + /** + * 密集匹配 测试构建树和查找 中文字符,并与wordTree对比效率 + */ + @Test + public void testFindCNChar() { + final StopWatch stopWatch = new StopWatch(); + final String input = "赵啊三在做什么"; + + final NFA NFALocal = new NFA(); + NFALocal.insert("赵", "赵啊", "赵啊三"); + NFALocal.buildAc(); + + stopWatch.start("automaton_cn_find"); + final List result = NFALocal.find(input); + stopWatch.stop(); + + Assert.assertEquals(3, result.size()); + Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(","))); + Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex()); + Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex()); + Assert.assertEquals(Integer.valueOf(0), result.get(1).getBeginIndex()); + Assert.assertEquals(Integer.valueOf(1), result.get(1).getEndIndex()); + Assert.assertEquals(Integer.valueOf(0), result.get(2).getBeginIndex()); + Assert.assertEquals(Integer.valueOf(2), result.get(2).getEndIndex()); + + final WordTree wordTreeLocal = new WordTree(); + wordTreeLocal.addWords("赵", "赵啊", "赵啊三"); + + stopWatch.start("wordtree_cn_find"); + final List result1 = wordTreeLocal.matchAllWords(input, -1, true, true).stream().map(FoundWord::getWord) + .collect(Collectors.toList()); + stopWatch.stop(); + + Assert.assertEquals(3, result1.size()); + Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1)); + + //Console.log(stopWatch.prettyPrint()); + } + + /** + * 非密集匹配 测试构建树和查找 中文字符,并与wordTree对比效率, + */ + @Test + public void testFindCNCharNotDensity() { + final StopWatch stopWatch = new StopWatch(); + final String input = "赵啊三在做什么"; + + final NFA NFALocal = new NFA(); + NFALocal.insert("赵", "赵啊", "赵啊三"); + NFALocal.buildAc(); + + stopWatch.start("automaton_cn_find_not_density"); + final List result = NFALocal.find(input, false); + stopWatch.stop(); + + Assert.assertEquals(1, result.size()); + Assert.assertEquals("赵", result.stream().map(FoundWord::getWord).collect(Collectors.joining(","))); + Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex()); + Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex()); + + final WordTree wordTreeLocal = new WordTree(); + wordTreeLocal.addWords("赵", "赵啊", "赵啊三"); + + stopWatch.start("wordtree_cn_find_not_density"); + final List result1 = + wordTreeLocal.matchAllWords(input, -1, false, true).stream().map(FoundWord::getWord) + .collect(Collectors.toList()); + stopWatch.stop(); + + Assert.assertEquals(1, result1.size()); + Assert.assertEquals("赵", String.join(",", result1)); + + //Console.log(stopWatch.prettyPrint()); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/text/split/StrSplitterTest.java b/hutool-core/src/test/java/cn/hutool/core/text/split/StrSplitterTest.java index 0cea8de30..69a059374 100644 --- a/hutool-core/src/test/java/cn/hutool/core/text/split/StrSplitterTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/split/StrSplitterTest.java @@ -1,5 +1,6 @@ package cn.hutool.core.text.split; +import cn.hutool.core.lang.Console; import org.junit.Assert; import org.junit.Test; @@ -59,16 +60,25 @@ public class StrSplitterTest { final String str = ""; final String[] split = str.split(","); final String[] strings = SplitUtil.splitToArray(str, ",", -1, false, false); + Assert.assertNotNull(strings); Assert.assertArrayEquals(split, strings); + + final String[] strings2 = SplitUtil.splitToArray(str, ",", -1, false, true); + Assert.assertEquals(0, strings2.length); } + @SuppressWarnings("ConstantValue") @Test public void splitNullTest(){ final String str = null; final String[] strings = SplitUtil.splitToArray(str, ",", -1, false, false); Assert.assertNotNull(strings); Assert.assertEquals(0, strings.length); + + final String[] strings2 = SplitUtil.splitToArray(str, ",", -1, false, true); + Assert.assertNotNull(strings2); + Assert.assertEquals(0, strings2.length); } /** diff --git a/hutool-core/src/test/java/cn/hutool/core/util/PageUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/PageUtilTest.java deleted file mode 100644 index 10ec440db..000000000 --- a/hutool-core/src/test/java/cn/hutool/core/util/PageUtilTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.hutool.core.util; - -import org.junit.Assert; -import org.junit.Test; - -/** - * 分页单元测试 - * - * @author Looly - */ -public class PageUtilTest { - - @Test - public void transToStartEndTest() { - final int[] startEnd1 = PageUtil.transToStartEnd(0, 10); - Assert.assertEquals(0, startEnd1[0]); - Assert.assertEquals(10, startEnd1[1]); - - final int[] startEnd2 = PageUtil.transToStartEnd(1, 10); - Assert.assertEquals(10, startEnd2[0]); - Assert.assertEquals(20, startEnd2[1]); - } - - @Test - public void totalPage() { - final int totalPage = PageUtil.totalPage(20, 3); - Assert.assertEquals(7, totalPage); - } - - @Test - public void rainbowTest() { - final int[] rainbow = PageUtil.rainbow(5, 20, 6); - Assert.assertArrayEquals(new int[]{3, 4, 5, 6, 7, 8}, rainbow); - } -} diff --git a/hutool-db/src/main/java/cn/hutool/db/Page.java b/hutool-db/src/main/java/cn/hutool/db/Page.java index 7e0c93836..176d7f42d 100644 --- a/hutool-db/src/main/java/cn/hutool/db/Page.java +++ b/hutool-db/src/main/java/cn/hutool/db/Page.java @@ -1,8 +1,8 @@ package cn.hutool.db; import cn.hutool.core.lang.Segment; +import cn.hutool.core.math.PageInfo; import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.PageUtil; import cn.hutool.db.sql.Order; import java.io.Serializable; @@ -16,6 +16,9 @@ import java.util.Arrays; public class Page implements Segment, Serializable { private static final long serialVersionUID = 97792549823353462L; + /** + * 默认 + */ public static final int DEFAULT_PAGE_SIZE = 20; /** @@ -140,15 +143,16 @@ public class Page implements Segment, Serializable { /** * @return 开始位置 - * @see #getStartIndex() + * @see #getBeginIndex() */ public int getStartPosition() { - return getStartIndex(); + return getBeginIndex(); } @Override - public Integer getStartIndex() { - return PageUtil.getStart(this.pageNumber, this.pageSize); + public Integer getBeginIndex() { + return PageInfo.of(Integer.MAX_VALUE, this.pageSize) + .setFirstPageNo(0).setPageNo(this.pageNumber).getBeginIndex(); } /** @@ -161,7 +165,7 @@ public class Page implements Segment, Serializable { @Override public Integer getEndIndex() { - return PageUtil.getEnd(this.pageNumber, this.pageSize); + return PageInfo.of(Integer.MAX_VALUE, this.pageSize).setFirstPageNo(0).getEndIndex(); } /** @@ -178,7 +182,9 @@ public class Page implements Segment, Serializable { * @return 第一个数为开始位置,第二个数为结束位置 */ public int[] getStartEnd() { - return PageUtil.transToStartEnd(pageNumber, pageSize); + final PageInfo pageInfo = PageInfo.of(Integer.MAX_VALUE, this.pageSize) + .setFirstPageNo(0).setPageNo(this.pageNumber); + return new int[]{pageInfo.getBeginIndex(), pageInfo.getEndIndexExclude()}; } @Override diff --git a/hutool-db/src/main/java/cn/hutool/db/PageResult.java b/hutool-db/src/main/java/cn/hutool/db/PageResult.java index cb410d263..70118f2a6 100644 --- a/hutool-db/src/main/java/cn/hutool/db/PageResult.java +++ b/hutool-db/src/main/java/cn/hutool/db/PageResult.java @@ -1,6 +1,6 @@ package cn.hutool.db; -import cn.hutool.core.util.PageUtil; +import cn.hutool.core.math.PageInfo; import java.util.ArrayList; @@ -16,7 +16,7 @@ public class PageResult extends ArrayList { public static final int DEFAULT_PAGE_SIZE = Page.DEFAULT_PAGE_SIZE; /** - * 页码,{@link PageUtil#getFirstPageNo()}表示第一页 + * 页码 */ private int page; /** @@ -65,7 +65,7 @@ public class PageResult extends ArrayList { this(page, pageSize); this.total = total; - this.totalPage = PageUtil.totalPage(total, pageSize); + this.totalPage = PageInfo.of(total, pageSize).getPages(); } //---------------------------------------------------------- Constructor end @@ -142,7 +142,7 @@ public class PageResult extends ArrayList { * @return 是否第一页 */ public boolean isFirst() { - return this.page == PageUtil.getFirstPageNo(); + return this.page == 0; } /** diff --git a/hutool-db/src/test/java/cn/hutool/db/DbTest.java b/hutool-db/src/test/java/cn/hutool/db/DbTest.java index 1d2f6047c..49d8da933 100644 --- a/hutool-db/src/test/java/cn/hutool/db/DbTest.java +++ b/hutool-db/src/test/java/cn/hutool/db/DbTest.java @@ -34,7 +34,8 @@ public class DbTest { @Test public void pageTest() { // 测试数据库中一共4条数据,第0页有3条,第1页有1条 - final List page0 = Db.of().page(Entity.of("user"), Page.of(0, 3)); + final List page0 = Db.of().page(Entity.of("user"), + Page.of(0, 3)); Assert.assertEquals(3, page0.size()); final List page1 = Db.of().page(Entity.of("user"), Page.of(1, 3)); diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java index e87bda2b1..d0e52df3b 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONUtilTest.java @@ -35,12 +35,12 @@ public class JSONUtilTest { } /** - * 数字解析为JSONObject忽略 + * 数字解析为JSONArray报错 */ - @Test + @Test(expected = JSONException.class) public void parseNumberTest2() { final JSONObject json = JSONUtil.parseObj(123L); - Assert.assertEquals(new JSONObject(), json); + Assert.assertNotNull(json); } @Test @@ -85,7 +85,7 @@ public class JSONUtilTest { @Test public void toJsonStrTest3() { // 验证某个字段为JSON字符串时转义是否规范 - final JSONObject object = new JSONObject(true); + final JSONObject object = new JSONObject(JSONConfig.of().setIgnoreError(true)); object.set("name", "123123"); object.set("value", "\\"); object.set("value2", "