From 4f811fc984c13164fd90f7d319be81031efc94e9 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 27 Sep 2019 19:35:39 +0800 Subject: [PATCH] fix StrBuilder bug --- CHANGELOG.md | 2 + .../java/cn/hutool/core/date/StopWatch.java | 2 +- .../java/cn/hutool/core/text/StrBuilder.java | 154 ++++++++++-------- .../cn/hutool/core/text/StrBuilderTest.java | 31 ++++ 4 files changed, 123 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b53b4f8de..c713bcf43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ * 【extra】 修复Mail中sslEnable无效问题(pr#74@Gitee) * 【extra】 修复CsvParser中最后一行双引号没有去除的问题(pr#73@Gitee) * 【crypto】 修复SM2算法在自定义密钥时无效问题(issue#I12P5I@Gitee) +* 【core】 修复StopWatch.prettyPrint条件问题(issue#I12RAC@Gitee) +* 【core】 修复StrBuilder.del无法删除最后一个字符的问题(issue#I12R14@Gitee) ------------------------------------------------------------------------------------------------------------- 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 a22f3dd21..517084322 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 @@ -312,7 +312,7 @@ public class StopWatch { public String prettyPrint() { StringBuilder sb = new StringBuilder(shortSummary()); sb.append(FileUtil.getLineSeparator()); - if (null != this.taskList) { + if (null == this.taskList) { sb.append("No task info kept"); } else { sb.append("---------------------------------------------").append(FileUtil.getLineSeparator()); diff --git a/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java b/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java index 2cfd1e2cc..3a9804787 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/StrBuilder.java @@ -1,12 +1,12 @@ package cn.hutool.core.text; -import java.io.Serializable; -import java.util.Arrays; - import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; +import java.io.Serializable; +import java.util.Arrays; + /** * 可复用的字符串生成器,非线程安全 * @@ -16,33 +16,42 @@ import cn.hutool.core.util.StrUtil; public class StrBuilder implements CharSequence, Appendable, Serializable { private static final long serialVersionUID = 6341229705927508451L; - /** 默认容量 */ + /** + * 默认容量 + */ public static final int DEFAULT_CAPACITY = 16; - /** 存放的字符数组 */ + /** + * 存放的字符数组 + */ private char[] value; - /** 当前指针位置,或者叫做已经加入的字符数,此位置总在最后一个字符之后 */ + /** + * 当前指针位置,或者叫做已经加入的字符数,此位置总在最后一个字符之后 + */ private int position; - + /** * 创建字符串构建器 + * * @return {@link StrBuilder} */ public static StrBuilder create() { return new StrBuilder(); } - + /** * 创建字符串构建器 + * * @param initialCapacity 初始容量 * @return {@link StrBuilder} */ public static StrBuilder create(int initialCapacity) { return new StrBuilder(initialCapacity); } - + /** * 创建字符串构建器 + * * @param strs 初始字符串 * @return {@link StrBuilder} * @since 4.0.1 @@ -52,6 +61,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { } // ------------------------------------------------------------------------------------ Constructor start + /** * 构造 */ @@ -67,7 +77,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { public StrBuilder(int initialCapacity) { value = new char[initialCapacity]; } - + /** * 构造 * @@ -76,16 +86,17 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { */ public StrBuilder(CharSequence... strs) { this(ArrayUtil.isEmpty(strs) ? DEFAULT_CAPACITY : (totalLength(strs) + DEFAULT_CAPACITY)); - for(int i = 0; i < strs.length; i++) { + for (int i = 0; i < strs.length; i++) { append(strs[i]); } } // ------------------------------------------------------------------------------------ Constructor end // ------------------------------------------------------------------------------------ Append + /** * 追加对象,对象会被转换为字符串 - * + * * @param obj 对象 * @return this */ @@ -106,7 +117,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 追加一个字符数组 - * + * * @param src 字符数组 * @return this */ @@ -119,8 +130,8 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 追加一个字符数组 - * - * @param src 字符数组 + * + * @param src 字符数组 * @param srcPos 开始位置(包括) * @param length 长度 * @return this @@ -140,9 +151,10 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { } // ------------------------------------------------------------------------------------ Insert + /** * 追加对象,对象会被转换为字符串 - * + * * @param obj 对象 * @return this */ @@ -155,9 +167,9 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 插入指定字符 - * + * * @param index 位置 - * @param c 字符 + * @param c 字符 * @return this */ public StrBuilder insert(int index, char c) { @@ -171,9 +183,9 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { * 指定位置插入数据
* 如果插入位置为当前位置,则定义为追加
* 如果插入位置大于当前位置,则中间部分补充空格 - * + * * @param index 插入位置 - * @param src 源数组 + * @param src 源数组 * @return this */ public StrBuilder insert(int index, char[] src) { @@ -187,9 +199,9 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { * 指定位置插入数据
* 如果插入位置为当前位置,则定义为追加
* 如果插入位置大于当前位置,则中间部分补充空格 - * - * @param index 插入位置 - * @param src 源数组 + * + * @param index 插入位置 + * @param src 源数组 * @param srcPos 位置 * @param length 长度 * @return this @@ -219,9 +231,9 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { * 指定位置插入字符串的某个部分
* 如果插入位置为当前位置,则定义为追加
* 如果插入位置大于当前位置,则中间部分补充空格 - * + * * @param index 位置 - * @param csq 字符串 + * @param csq 字符串 * @return this */ public StrBuilder insert(int index, CharSequence csq) { @@ -251,11 +263,11 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { * 指定位置插入字符串的某个部分
* 如果插入位置为当前位置,则定义为追加
* 如果插入位置大于当前位置,则中间部分补充空格 - * + * * @param index 位置 - * @param csq 字符串 + * @param csq 字符串 * @param start 字符串开始位置(包括) - * @param end 字符串结束位置(不包括) + * @param end 字符串结束位置(不包括) * @return this */ public StrBuilder insert(int index, CharSequence csq, int start, int end) { @@ -289,12 +301,13 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { } // ------------------------------------------------------------------------------------ Others + /** * 将指定段的字符列表写出到目标字符数组中 - * + * * @param srcBegin 起始位置(包括) - * @param srcEnd 结束位置(不包括) - * @param dst 目标数组 + * @param srcEnd 结束位置(不包括) + * @param dst 目标数组 * @param dstBegin 目标起始位置(包括) * @return this */ @@ -316,7 +329,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 是否有内容 - * + * * @return 是否有内容 */ public boolean hasContent() { @@ -325,16 +338,16 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 是否为空 - * + * * @return 是否为空 */ public boolean isEmpty() { return position == 0; } - + /** * 删除全部字符,位置归零 - * + * * @return this */ public StrBuilder clear() { @@ -343,7 +356,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 删除全部字符,位置归零 - * + * * @return this */ public StrBuilder reset() { @@ -354,51 +367,61 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 删除到指定位置
* 如果新位置小于等于0,则删除全部 - * + * * @param newPosition 新的位置,不包括这个位置 * @return this */ public StrBuilder delTo(int newPosition) { if (newPosition < 0) { - this.reset(); - } else if (newPosition < this.position) { - this.position = newPosition; + newPosition = 0; } - return this; + return del(newPosition, this.position); } /** - * 删除指定长度的字符 - * - * @param start 开始位置(包括) - * @param end 结束位置(不包括) + * 删除指定长度的字符,规则如下: + * + *
+	 * 1、end大于等于最大长度,结束按照最大长度计算,相当于删除start之后虽有部分(性能最好)
+	 * 2、end小于start时,抛出StringIndexOutOfBoundsException
+	 * 3、start小于0 按照0处理
+	 * 4、start等于end不处理
+	 * 5、start和end都位于长度区间内,删除这段内容(内存拷贝)
+	 * 
+ * + * @param start 开始位置,负数按照0处理(包括) + * @param end 结束位置,超出最大长度按照最大长度处理(不包括) * @return this + * @throws StringIndexOutOfBoundsException 当start > end抛出此异常 */ - public StrBuilder del(int start, int end) { + public StrBuilder del(int start, int end) throws StringIndexOutOfBoundsException { if (start < 0) { start = 0; } - if (end > this.position) { - end = this.position; - } - if (start > end) { - throw new StringIndexOutOfBoundsException("Start is greater than End."); - } - if (end == this.position) { + + if (end >= this.position) { + // end在边界及以外,相当于删除后半部分 this.position = start; + return this; + } else if (end < 0) { + // start和end都为0的情况下表示删除全部 + end = 0; } int len = end - start; + // 截取中间部分,需要将后半部分复制到删除的开始位置 if (len > 0) { System.arraycopy(value, start + len, value, start, this.position - end); this.position -= len; + } else if (len < 0) { + throw new StringIndexOutOfBoundsException("Start is greater than End."); } return this; } /** * 生成字符串 - * + * * @param isReset 是否重置,重置后相当于空的构建器 * @return 生成的字符串 */ @@ -415,7 +438,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 重置并返回生成的字符串 - * + * * @return 字符串 */ public String toStringAndReset() { @@ -450,7 +473,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 返回自定段的字符串 - * + * * @param start 开始位置(包括) * @return this */ @@ -460,9 +483,9 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 返回自定段的字符串 - * + * * @param start 开始位置(包括) - * @param end 结束位置(不包括) + * @param end 结束位置(不包括) * @return this */ public String subString(int start, int end) { @@ -470,10 +493,11 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { } // ------------------------------------------------------------------------------------ Private method start + /** * 指定位置之后的数据后移指定长度 - * - * @param index 位置 + * + * @param index 位置 * @param length 位移长度 */ private void moveDataAfterIndex(int index, int length) { @@ -490,7 +514,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 确认容量是否够用,不够用则扩展容量 - * + * * @param minimumCapacity 最小容量 */ private void ensureCapacity(int minimumCapacity) { @@ -502,7 +526,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { /** * 扩展容量
* 首先对容量进行二倍扩展,如果小于最小容量,则扩展为最小容量 - * + * * @param minimumCapacity 需要扩展的最小容量 */ private void expandCapacity(int minimumCapacity) { @@ -519,18 +543,18 @@ public class StrBuilder implements CharSequence, Appendable, Serializable { } value = Arrays.copyOf(value, newCapacity); } - + /** * 给定字符串数组的总长度
* null字符长度定义为0 - * + * * @param strs 字符串数组 * @return 总长度 * @since 4.0.1 */ private static int totalLength(CharSequence... strs) { int totalLength = 0; - for(int i = 0 ; i < strs.length; i++) { + for (int i = 0; i < strs.length; i++) { totalLength += (null == strs[i] ? 4 : strs[i].length()); } return totalLength; diff --git a/hutool-core/src/test/java/cn/hutool/core/text/StrBuilderTest.java b/hutool-core/src/test/java/cn/hutool/core/text/StrBuilderTest.java index a81262abf..a812f4c22 100644 --- a/hutool-core/src/test/java/cn/hutool/core/text/StrBuilderTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/StrBuilderTest.java @@ -86,4 +86,35 @@ public class StrBuilderTest { builder.append(123).append(456.123D).append(true).append('\n'); Assert.assertEquals("123456.123true\n", builder.toString()); } + + @Test + public void delTest() { + // 删除全部测试 + StrBuilder strBuilder = new StrBuilder("ABCDEFG"); + int length = strBuilder.length(); + StrBuilder builder = strBuilder.del(0, length); + Assert.assertEquals("", builder.toString()); + } + + @Test + public void delTest2() { + // 删除中间部分测试 + StrBuilder strBuilder = new StrBuilder("ABCDEFG"); + int length = strBuilder.length(); + StrBuilder builder = strBuilder.del(2,6); + Assert.assertEquals("ABG", builder.toString()); + } + + @Test + public void delToTest() { + StrBuilder strBuilder = new StrBuilder("ABCDEFG"); + + // 不处理 + StrBuilder builder = strBuilder.delTo(7); + Assert.assertEquals("ABCDEFG", builder.toString()); + + // 删除全部 + builder = strBuilder.delTo(0); + Assert.assertEquals("", builder.toString()); + } }