From c0461c16f61bdf50d6f00c3082e1017c43246664 Mon Sep 17 00:00:00 2001 From: "zongze.lee" Date: Thu, 6 Nov 2025 02:09:57 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=9B=B8=E9=82=BB=E5=8C=BA=E9=97=B4=E5=90=88=E5=B9=B6=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对于测试用例: range1 = [1, 3],其上界是 Bound.atMost(3)(右闭区间) range2 = [3, 5],其下界是 Bound.atLeast(3)(左闭区间) 在比较 Bound.atMost(3).compareTo(Bound.atLeast(3)) 时: 两者的值相等(都是3) 调用 compareIfSameBoundValue 方法 bt1 = CLOSE_UPPER_BOUND (code=2) bt2 = CLOSE_LOWER_BOUND (code=-2) 两者是错位的(一个上界一个下界) 由于 bt1.isLowerBound() 为 false(因为是上界),所以返回 -1 这导致 boundedRange.getUpperBound().compareTo(other.getLowerBound()) < 0 为 true,判定区间不相交。 但实际上,[1,3] 和 [3,5] 在点3处是相交的,因为3既包含在第一个区间又包含在第二个区间中。 --- .../cn/hutool/v7/core/lang/range/FiniteBound.java | 8 +++++++- .../cn/hutool/v7/core/lang/range/BoundTest.java | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/hutool-core/src/main/java/cn/hutool/v7/core/lang/range/FiniteBound.java b/hutool-core/src/main/java/cn/hutool/v7/core/lang/range/FiniteBound.java index 2fbe5c5309..e8ec3221e0 100644 --- a/hutool-core/src/main/java/cn/hutool/v7/core/lang/range/FiniteBound.java +++ b/hutool-core/src/main/java/cn/hutool/v7/core/lang/range/FiniteBound.java @@ -200,8 +200,14 @@ class FiniteBound> implements Bound { if (bt1 == bt2) { return 0; } - // 一为左边界,一为右边界,则左边界恒在右边界后 + // 一为左边界,一为右边界 if (bt1.isDislocated(bt2)) { + // 特殊情况:右闭区间与左闭区间在同一点时,认为它们重合(用于区间相交判断) + if ((bt1 == BoundType.CLOSE_UPPER_BOUND && bt2 == BoundType.CLOSE_LOWER_BOUND) || + (bt1 == BoundType.CLOSE_LOWER_BOUND && bt2 == BoundType.CLOSE_UPPER_BOUND)) { + return 0; + } + // 一般情况:左边界恒在右边界后 return bt1.isLowerBound() ? 1 : -1; } // 都为左边界,则封闭边界在前,若都为右边界,则封闭边界在后 diff --git a/hutool-core/src/test/java/cn/hutool/v7/core/lang/range/BoundTest.java b/hutool-core/src/test/java/cn/hutool/v7/core/lang/range/BoundTest.java index b400a0476e..6e69865661 100644 --- a/hutool-core/src/test/java/cn/hutool/v7/core/lang/range/BoundTest.java +++ b/hutool-core/src/test/java/cn/hutool/v7/core/lang/range/BoundTest.java @@ -17,14 +17,28 @@ package cn.hutool.v7.core.lang.range; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + /** * test for {@link Bound} */ @SuppressWarnings("EqualsWithItself") public class BoundTest { + @Test + @DisplayName("测试相邻区间合并") + void testUnionIfIntersectedWithAdjacentRanges() { + BoundedRange range1 = BoundedRange.close(1, 3); + BoundedRange range2 = BoundedRange.close(3, 5); + BoundedRange result = BoundedRangeOperation.unionIfIntersected(range1, range2); + + assertEquals(Bound.atLeast(1), result.getLowerBound()); + assertEquals(Bound.atMost(5), result.getUpperBound()); + } + @Test public void testEquals() { final Bound bound = new FiniteBound<>(1, BoundType.OPEN_UPPER_BOUND); -- Gitee From 56ffa92e5e8448cda8dbb6bd0d540182a2f48fe4 Mon Sep 17 00:00:00 2001 From: "zongze.lee" Date: Thu, 6 Nov 2025 02:45:40 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E4=B8=8D=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E8=B5=B7=E5=A7=8B=E5=85=83=E7=B4=A0=E7=9A=84=E8=BF=AD?= =?UTF-8?q?=E4=BB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 内存溢出 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.base/java.util.Arrays.copyOf(Arrays.java:3481) at java.base/java.util.ArrayList.grow(ArrayList.java:237) at java.base/java.util.ArrayList.grow(ArrayList.java:244) at java.base/java.util.ArrayList.add(ArrayList.java:454) at java.base/java.util.ArrayList.add(ArrayList.java:467) at cn.hutool.v7.core.lang.range.RangeTest.testIteratorWithoutIncludeStart(RangeTest.java:49) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) --- .../cn/hutool/v7/core/lang/range/Range.java | 4 ++ .../hutool/v7/core/lang/range/RangeTest.java | 60 ++++++++++++------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/v7/core/lang/range/Range.java b/hutool-core/src/main/java/cn/hutool/v7/core/lang/range/Range.java index 397c5256f9..26a8cef8cc 100644 --- a/hutool-core/src/main/java/cn/hutool/v7/core/lang/range/Range.java +++ b/hutool-core/src/main/java/cn/hutool/v7/core/lang/range/Range.java @@ -165,6 +165,10 @@ public class Range implements Iterable, Serializable { * @return 下一步进 */ private T safeStep(final T base) { + // 添加边界检查 + if (base == null || (base.equals(end))) { + return null; + } final int index = this.index; T next = null; try { diff --git a/hutool-core/src/test/java/cn/hutool/v7/core/lang/range/RangeTest.java b/hutool-core/src/test/java/cn/hutool/v7/core/lang/range/RangeTest.java index 0c9be907bf..be8f2e37a2 100644 --- a/hutool-core/src/test/java/cn/hutool/v7/core/lang/range/RangeTest.java +++ b/hutool-core/src/test/java/cn/hutool/v7/core/lang/range/RangeTest.java @@ -22,13 +22,17 @@ import cn.hutool.v7.core.date.DateTime; import cn.hutool.v7.core.date.DateUtil; import cn.hutool.v7.core.text.StrUtil; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; +import static org.junit.jupiter.api.Assertions.assertEquals; + /** * {@link Range} 单元测试 * @@ -36,6 +40,18 @@ import java.util.NoSuchElementException; */ public class RangeTest { + @Test + @DisplayName("测试不包含起始元素的迭代") + void testIteratorWithoutIncludeStart() { + Range range = new Range<>(1, 5, (current, end, index) -> current + 1, false, true); + List elements = new ArrayList<>(); + for (Integer i : range) { + elements.add(i); + } + + assertEquals(List.of(2, 3, 4, 5), elements); + } + @Test public void dateRangeTest() { final DateTime start = DateUtil.parse("2017-01-01"); @@ -50,9 +66,9 @@ public class RangeTest { final Iterator iterator = range.iterator(); Assertions.assertTrue(iterator.hasNext()); - Assertions.assertEquals(DateUtil.parse("2017-01-01"), iterator.next()); + assertEquals(DateUtil.parse("2017-01-01"), iterator.next()); Assertions.assertTrue(iterator.hasNext()); - Assertions.assertEquals(DateUtil.parse("2017-01-02"), iterator.next()); + assertEquals(DateUtil.parse("2017-01-02"), iterator.next()); Assertions.assertFalse(iterator.hasNext()); } @@ -75,11 +91,11 @@ public class RangeTest { final StringBuilder sb = new StringBuilder(); DateUtil.rangeConsume(start, end, DateField.DAY_OF_YEAR, a -> sb.append(DateTime.of(a).dayOfMonth()).append("#")); - Assertions.assertEquals("1#2#3#", sb.toString()); + assertEquals("1#2#3#", sb.toString()); final StringBuilder sb2 = new StringBuilder(); DateUtil.rangeConsume(null, null, DateField.DAY_OF_YEAR, a -> sb2.append(DateTime.of(a).dayOfMonth()).append("#")); - Assertions.assertEquals(StrUtil.EMPTY, sb2.toString()); + assertEquals(StrUtil.EMPTY, sb2.toString()); } @Test @@ -90,11 +106,11 @@ public class RangeTest { final DateRange range = DateUtil.range(start, end, DateField.MONTH); final Iterator iterator = range.iterator(); Assertions.assertTrue(iterator.hasNext()); - Assertions.assertEquals(DateUtil.parse("2021-01-31"), iterator.next()); + assertEquals(DateUtil.parse("2021-01-31"), iterator.next()); Assertions.assertTrue(iterator.hasNext()); - Assertions.assertEquals(DateUtil.parse("2021-02-28"), iterator.next()); + assertEquals(DateUtil.parse("2021-02-28"), iterator.next()); Assertions.assertTrue(iterator.hasNext()); - Assertions.assertEquals(DateUtil.parse("2021-03-31"), iterator.next()); + assertEquals(DateUtil.parse("2021-03-31"), iterator.next()); Assertions.assertFalse(iterator.hasNext()); } @@ -104,7 +120,7 @@ public class RangeTest { final Iterator iterator = range.iterator(); Assertions.assertTrue(iterator.hasNext()); - Assertions.assertEquals(Integer.valueOf(1), iterator.next()); + assertEquals(Integer.valueOf(1), iterator.next()); Assertions.assertFalse(iterator.hasNext()); } @@ -116,9 +132,9 @@ public class RangeTest { // 测试包含开始和结束情况下步进为1的情况 DateRange range = DateUtil.range(start, end, DateField.DAY_OF_YEAR); Iterator iterator = range.iterator(); - Assertions.assertEquals(iterator.next(), DateUtil.parse("2017-01-01")); - Assertions.assertEquals(iterator.next(), DateUtil.parse("2017-01-02")); - Assertions.assertEquals(iterator.next(), DateUtil.parse("2017-01-03")); + assertEquals(iterator.next(), DateUtil.parse("2017-01-01")); + assertEquals(iterator.next(), DateUtil.parse("2017-01-02")); + assertEquals(iterator.next(), DateUtil.parse("2017-01-03")); try { iterator.next(); Assertions.fail("已超过边界,下一个元素不应该存在!"); @@ -128,8 +144,8 @@ public class RangeTest { // 测试多步进的情况 range = new DateRange(start, end, DateField.DAY_OF_YEAR, 2); iterator = range.iterator(); - Assertions.assertEquals(DateUtil.parse("2017-01-01"), iterator.next()); - Assertions.assertEquals(DateUtil.parse("2017-01-03"), iterator.next()); + assertEquals(DateUtil.parse("2017-01-01"), iterator.next()); + assertEquals(DateUtil.parse("2017-01-03"), iterator.next()); } @Test @@ -140,9 +156,9 @@ public class RangeTest { // 测试不包含开始结束时间的情况 final DateRange range = new DateRange(start, end, DateField.DAY_OF_YEAR, 1, false, false); final Iterator iterator = range.iterator(); - Assertions.assertEquals(DateUtil.parse("2017-01-02"), iterator.next()); - Assertions.assertEquals(DateUtil.parse("2017-01-03"), iterator.next()); - Assertions.assertEquals(DateUtil.parse("2017-01-04"), iterator.next()); + assertEquals(DateUtil.parse("2017-01-02"), iterator.next()); + assertEquals(DateUtil.parse("2017-01-03"), iterator.next()); + assertEquals(DateUtil.parse("2017-01-04"), iterator.next()); try { iterator.next(); Assertions.fail("不包含结束时间情况下,下一个元素不应该存在!"); @@ -156,8 +172,8 @@ public class RangeTest { final Date end = DateUtil.parse("2017-01-31"); final List rangeToList = DateUtil.rangeToList(start, end, DateField.DAY_OF_YEAR); - Assertions.assertEquals(DateUtil.parse("2017-01-01"), rangeToList.get(0)); - Assertions.assertEquals(DateUtil.parse("2017-01-02"), rangeToList.get(1)); + assertEquals(DateUtil.parse("2017-01-01"), rangeToList.get(0)); + assertEquals(DateUtil.parse("2017-01-02"), rangeToList.get(1)); } @@ -173,8 +189,8 @@ public class RangeTest { final DateRange endRange = DateUtil.range(start1, end1, DateField.DAY_OF_YEAR); // 交集 final List dateTimes = DateUtil.rangeContains(startRange, endRange); - Assertions.assertEquals(1, dateTimes.size()); - Assertions.assertEquals(DateUtil.parse("2017-01-31"), dateTimes.get(0)); + assertEquals(1, dateTimes.size()); + assertEquals(DateUtil.parse("2017-01-31"), dateTimes.get(0)); } @Test @@ -190,8 +206,8 @@ public class RangeTest { // 差集 final List dateTimes1 = DateUtil.rangeNotContains(startRange, endRange); - Assertions.assertEquals(1, dateTimes1.size()); - Assertions.assertEquals(DateUtil.parse("2017-01-31"), dateTimes1.get(0)); + assertEquals(1, dateTimes1.size()); + assertEquals(DateUtil.parse("2017-01-31"), dateTimes1.get(0)); } } -- Gitee