This commit is contained in:
Looly 2022-10-08 18:21:22 +08:00
parent 910caa2f06
commit 3835e6298f
11 changed files with 334 additions and 294 deletions

View File

@ -326,4 +326,30 @@ public class CompareUtil {
final IndexedComparator<U> indexedComparator = new IndexedComparator<>(atEndIfMiss, objs);
return (o1, o2) -> indexedComparator.compare(keyExtractor.apply(o1), keyExtractor.apply(o2));
}
/**
* 取两个值中的最小值大小相同返回第一个值
*
* @param <T> 值类型
* @param t1 第一个值
* @param t2 第二个值
* @return 最小值
* @since 6.0.0
*/
public static <T extends Comparable<? super T>> T min(final T t1, final T t2) {
return compare(t1, t2) <= 0 ? t1 : t2;
}
/**
* 取两个值中的最大值大小相同返回第一个值
*
* @param <T> 值类型
* @param t1 第一个值
* @param t2 第二个值
* @return 最大值
* @since 6.0.0
*/
public static <T extends Comparable<? super T>> T max(final T t1, final T t2) {
return compare(t1, t2) >= 0 ? t1 : t2;
}
}

View File

@ -1,6 +1,6 @@
package cn.hutool.core.date;
import cn.hutool.core.lang.Range;
import cn.hutool.core.lang.range.Range;
import java.util.Date;

View File

@ -8,7 +8,7 @@ import java.util.function.Predicate;
/**
* <p>边界对象描述具有特定上界或下界的单侧无界的区间
*
* <h3>边界的类型</h3>
* <p>边界的类型</p>
* <p>边界根据其{@link #getType()}所获得的类型可用于描述基于边界值<em>t</em>的不等式:
* <ul>
* <li>{@link #noneLowerBound()}{@code {x | x > -}}</li>
@ -20,7 +20,7 @@ import java.util.function.Predicate;
* </ul>
* 当作为{@link Predicate}使用时可用于判断入参对象是否能满足当前实例所对应的不等式
*
* <h3>边界的比较</h3>
* <p>边界的比较</p>
* <p>边界对象本身实现了{@link Comparable}接口
* 当使用{@link Comparable#compareTo}比较两个边界对象时
* 返回的比较值表示两个边界对象对应的点在实数轴上从左到右的先后顺序<br>
@ -41,27 +41,16 @@ import java.util.function.Predicate;
public interface Bound<T extends Comparable<? super T>> extends Predicate<T>, Comparable<Bound<T>> {
/**
* 无穷小
* 无穷小的描述
*/
String INFINITE_MIN = "-\u221e";
/**
* 无穷大
* 无穷大的藐视
*/
String INFINITE_MAX = "+\u221e";
/**
* 无限小的左边界
*/
@SuppressWarnings("rawtypes")
NoneLowerBound NONE_LOWER_BOUND = new NoneLowerBound();
/**
* 无限大的右边界
*/
@SuppressWarnings("rawtypes")
NoneUpperBound NONE_UPPER_BOUND = new NoneUpperBound();
// region --------------- static methods
/**
* {@code {x | x > -}}
*
@ -70,7 +59,7 @@ public interface Bound<T extends Comparable<? super T>> extends Predicate<T>, Co
*/
@SuppressWarnings("unchecked")
static <T extends Comparable<? super T>> Bound<T> noneLowerBound() {
return NONE_LOWER_BOUND;
return NoneLowerBound.INSTANCE;
}
/**
@ -81,7 +70,7 @@ public interface Bound<T extends Comparable<? super T>> extends Predicate<T>, Co
*/
@SuppressWarnings("unchecked")
static <T extends Comparable<? super T>> Bound<T> noneUpperBound() {
return NONE_UPPER_BOUND;
return NoneUpperBound.INSTANCE;
}
/**
@ -127,6 +116,7 @@ public interface Bound<T extends Comparable<? super T>> extends Predicate<T>, Co
static <T extends Comparable<? super T>> Bound<T> atMost(final T max) {
return new FiniteBound<>(Objects.requireNonNull(max), BoundType.CLOSE_UPPER_BOUND);
}
// endregion --------------- static methods
/**
* 获取边界值
@ -148,6 +138,7 @@ public interface Bound<T extends Comparable<? super T>> extends Predicate<T>, Co
* @param t 要检验的值不允许为{@code null}
* @return 是否
*/
@SuppressWarnings("AbstractMethodOverridesAbstractMethod")
@Override
boolean test(T t);
@ -163,6 +154,7 @@ public interface Bound<T extends Comparable<? super T>> extends Predicate<T>, Co
* @param bound 边界
* @return 位置
*/
@SuppressWarnings("AbstractMethodOverridesAbstractMethod")
@Override
int compareTo(final Bound<T> bound);
@ -277,11 +269,11 @@ public interface Bound<T extends Comparable<? super T>> extends Predicate<T>, Co
@Override
public int compareTo(final Bound<T> bound) {
// 另一边界为无限小的左边界则当前边界必然靠后
if (bound instanceof Bound.NoneLowerBound) {
if (bound instanceof NoneLowerBound) {
return 1;
}
// 另一边界为无限大的右边界则当前边界必然靠前
if (bound instanceof Bound.NoneUpperBound) {
if (bound instanceof NoneUpperBound) {
return -1;
}
// 两值不相等直接比较边界值
@ -307,8 +299,7 @@ public interface Bound<T extends Comparable<? super T>> extends Predicate<T>, Co
return bt1.isLowerBound() ? 1 : -1;
}
// 都为左边界则封闭边界在前若都为右边界则封闭边界在后
return bt1.isLowerBound() ?
Integer.compare(bt1.getCode(), bt2.getCode()) : Integer.compare(bt1.getCode(), bt2.getCode());
return Integer.compare(bt1.getCode(), bt2.getCode());
}
/**
@ -383,205 +374,4 @@ public interface Bound<T extends Comparable<? super T>> extends Predicate<T>, Co
);
}
}
/**
* 无限小的左边界
*
* @param <T> 边界值类型
*/
class NoneLowerBound<T extends Comparable<? super T>> implements Bound<T> {
private NoneLowerBound() {
}
/**
* 获取边界值
*
* @return 边界值
*/
@Override
public T getValue() {
return null;
}
/**
* 获取边界类型
*
* @return 边界类型
*/
@Override
public BoundType getType() {
return BoundType.OPEN_LOWER_BOUND;
}
/**
* 检验指定值是否在当前边界表示的范围内
*
* @param t 要检验的值不允许为{@code null}
* @return 是否
*/
@Override
public boolean test(final T t) {
return true;
}
/**
* <p>比较另一边界与当前边界在坐标轴上位置的先后顺序<br>
* 若令当前边界为<em>t1</em>另一边界为<em>t2</em>则有
* <ul>
* <li>-1<em>t1</em><em>t2</em>的左侧</li>
* <li>0<em>t1</em><em>t2</em>的重合</li>
* <li>-1<em>t1</em><em>t2</em>的右侧</li>
* </ul>
*
* @param bound 边界
* @return 位置
*/
@Override
public int compareTo(final Bound<T> bound) {
return bound instanceof Bound.NoneLowerBound ? 0 : -1;
}
/**
* 获取{@code "[value"}{@code "(value"}格式的字符串
*
* @return 字符串
*/
@Override
public String descBound() {
return getType().getSymbol() + INFINITE_MIN;
}
/**
* 对当前边界取反
*
* @return 取反后的边界
*/
@Override
public Bound<T> negate() {
return this;
}
/**
* 将当前实例转为一个区间
*
* @return 区间
*/
@Override
public BoundedRange<T> toRange() {
return BoundedRange.all();
}
/**
* 获得当前实例对应的{@code { x | x >= xxx}}格式的不等式字符串
*
* @return 字符串
*/
@Override
public String toString() {
return "{x | x > -\u221e}";
}
}
/**
* 无限大的右边界
*
* @param <T> 边界值类型
*/
class NoneUpperBound<T extends Comparable<? super T>> implements Bound<T> {
private NoneUpperBound() {
}
/**
* 获取边界值
*
* @return 边界值
*/
@Override
public T getValue() {
return null;
}
/**
* 获取边界类型
*
* @return 边界类型
*/
@Override
public BoundType getType() {
return BoundType.OPEN_UPPER_BOUND;
}
/**
* 检验指定值是否在当前边界表示的范围内
*
* @param t 要检验的值不允许为{@code null}
* @return 是否
*/
@Override
public boolean test(final T t) {
return true;
}
/**
* <p>比较另一边界与当前边界在坐标轴上位置的先后顺序<br>
* 若令当前边界为<em>t1</em>另一边界为<em>t2</em>则有
* <ul>
* <li>-1<em>t1</em><em>t2</em>的左侧</li>
* <li>0<em>t1</em><em>t2</em>的重合</li>
* <li>-1<em>t1</em><em>t2</em>的右侧</li>
* </ul>
*
* @param bound 边界
* @return 位置
*/
@Override
public int compareTo(final Bound<T> bound) {
return bound instanceof Bound.NoneUpperBound ? 0 : 1;
}
/**
* 获取{@code "[value"}{@code "(value"}格式的字符串
*
* @return 字符串
*/
@Override
public String descBound() {
return INFINITE_MAX + getType().getSymbol();
}
/**
* 获得当前实例对应的{@code { x | x >= xxx}}格式的不等式字符串
*
* @return 字符串
*/
@Override
public String toString() {
return "{x | x < +\u221e}";
}
/**
* 对当前边界取反
*
* @return 取反后的边界
*/
@Override
public Bound<T> negate() {
return this;
}
/**
* 将当前实例转为一个区间
*
* @return 区间
*/
@Override
public BoundedRange<T> toRange() {
return BoundedRange.all();
}
}
}

View File

@ -1,7 +1,7 @@
package cn.hutool.core.lang.range;
/**
* 边界类型
* 边界类型枚举
*
* @author huangchengxing
* @since 6.0.0
@ -45,6 +45,9 @@ public enum BoundType {
/**
* 构造
* @param symbol 符号`[``(`
* @param operator 运算符`<`
* @param code 是否为开区间
*/
BoundType(final String symbol, final String operator, final int code) {
this.symbol = symbol;
@ -85,7 +88,7 @@ public enum BoundType {
* @param boundType 另一边界类型
* @return 是否
*/
public boolean isDislocated(BoundType boundType) {
public boolean isDislocated(final BoundType boundType) {
return code * boundType.code < 0;
}

View File

@ -1,5 +1,6 @@
package cn.hutool.core.lang.range;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
@ -10,8 +11,8 @@ import java.util.function.Predicate;
* <p>参考<em>Guava</em><em>Range</em>实现用于描述作为上下界的两个{@link Bound}实例围成的一段区间<br>
* 作为{@link Predicate}使用时可检验指定值是否在区间中即指定值是否同时满足上下界的{@link Bound#test}方法
*
* <h3>区间的类型</h3>
* <p>支持通过工厂方法创建下述几种类型的区间
* <p>区间的类型支持通过工厂方法创建下述几种类型的区间</p>
* <table summary="">
* <tr><th>区间 <th>数学定义 <th>工厂方法
* <tr><td>{@code (a, b)} <td>{@code {x | a < x < b}} <td>{@link #open}
* <tr><td>{@code [a, b]} <td>{@code {x | a <= x <= b}}<td>{@link #close}
@ -24,7 +25,7 @@ import java.util.function.Predicate;
* <tr><td>{@code (-, +)}<td>{@code {x}} <td>{@link #all}
* </table>
*
* <h3>空区间</h3>
* <p>空区间</p>
* <p>根据数学定义当区间中无任何实数时认为该区间代表的集合为空集
* 用户可通过{@link #isEmpty}确认当前实例是否为空区间<br>
* 若实例上界<em>a</em>下界为<em>b</em>则当实例满足下述任意条件时认为其为一个空区间
@ -49,7 +50,7 @@ public class BoundedRange<T extends Comparable<? super T>> implements Predicate<
/**
* 双向无界的区间
*/
@SuppressWarnings("rawtypes")
@SuppressWarnings({"rawtypes", "unchecked"})
private static final BoundedRange ALL = new BoundedRange(Bound.noneLowerBound(), Bound.noneUpperBound());
/**
@ -287,12 +288,12 @@ public class BoundedRange<T extends Comparable<? super T>> implements Predicate<
* @return 是否
*/
public boolean isEmpty() {
Bound<T> low = getLowerBound();
Bound<T> up = getUpperBound();
if (low instanceof Bound.NoneLowerBound || up instanceof Bound.NoneUpperBound) {
final Bound<T> low = getLowerBound();
final Bound<T> up = getUpperBound();
if (low instanceof NoneLowerBound || up instanceof NoneUpperBound) {
return false;
}
int compareValue = low.getValue().compareTo(up.getValue());
final int compareValue = low.getValue().compareTo(up.getValue());
if (compareValue < 0) {
return false;
}
@ -450,8 +451,8 @@ public class BoundedRange<T extends Comparable<? super T>> implements Predicate<
return this;
}
return new BoundedRange<>(
getMin(getLowerBound(), other.getLowerBound()),
getMax(getUpperBound(), other.getUpperBound())
CompareUtil.min(getLowerBound(), other.getLowerBound()),
CompareUtil.max(getUpperBound(), other.getUpperBound())
);
}
@ -464,8 +465,8 @@ public class BoundedRange<T extends Comparable<? super T>> implements Predicate<
public BoundedRange<T> span(final BoundedRange<T> other) {
Objects.requireNonNull(other);
return new BoundedRange<>(
getMin(getLowerBound(), other.getLowerBound()),
getMax(getUpperBound(), other.getUpperBound())
CompareUtil.min(getLowerBound(), other.getLowerBound()),
CompareUtil.max(getUpperBound(), other.getUpperBound())
);
}
@ -481,8 +482,8 @@ public class BoundedRange<T extends Comparable<? super T>> implements Predicate<
return null;
}
return new BoundedRange<>(
getMin(getUpperBound(), other.getUpperBound()).negate(),
getMax(getLowerBound(), other.getLowerBound()).negate()
CompareUtil.min(getUpperBound(), other.getUpperBound()).negate(),
CompareUtil.max(getLowerBound(), other.getLowerBound()).negate()
);
}
@ -498,8 +499,8 @@ public class BoundedRange<T extends Comparable<? super T>> implements Predicate<
return null;
}
return new BoundedRange<>(
getMax(getLowerBound(), other.getLowerBound()),
getMin(getUpperBound(), other.getUpperBound())
CompareUtil.min(getLowerBound(), other.getLowerBound()),
CompareUtil.max(getUpperBound(), other.getUpperBound())
);
}
@ -557,15 +558,7 @@ public class BoundedRange<T extends Comparable<? super T>> implements Predicate<
// endregion
private static <T extends Comparable<? super T>> Bound<T> getMin(Bound<T> b1, Bound<T> b2) {
return b1.compareTo(b2) <= 0 ? b1 : b2;
}
private static <T extends Comparable<? super T>> Bound<T> getMax(Bound<T> b1, Bound<T> b2) {
return b1.compareTo(b2) >= 0 ? b1 : b2;
}
private static <T extends Comparable<? super T>> BoundedRange<T> checkEmpty(BoundedRange<T> range) {
private static <T extends Comparable<? super T>> BoundedRange<T> checkEmpty(final BoundedRange<T> range) {
Assert.isFalse(range.isEmpty(), "{} is a empty range", range);
return range;
}

View File

@ -0,0 +1,108 @@
package cn.hutool.core.lang.range;
/**
* 无限小的左边界
*
* @param <T> 边界值类型
* @author huangchengxing
* @since 6.0.0
*/
class NoneLowerBound<T extends Comparable<? super T>> implements Bound<T> {
/**
* 无限小的左边界单例
*/
@SuppressWarnings("rawtypes")
static final NoneLowerBound INSTANCE = new NoneLowerBound();
private NoneLowerBound() {
}
/**
* 获取边界值
*
* @return 边界值
*/
@Override
public T getValue() {
return null;
}
/**
* 获取边界类型
*
* @return 边界类型
*/
@Override
public BoundType getType() {
return BoundType.OPEN_LOWER_BOUND;
}
/**
* 检验指定值是否在当前边界表示的范围内
*
* @param t 要检验的值不允许为{@code null}
* @return 是否
*/
@Override
public boolean test(final T t) {
return true;
}
/**
* <p>比较另一边界与当前边界在坐标轴上位置的先后顺序<br>
* 若令当前边界为<em>t1</em>另一边界为<em>t2</em>则有
* <ul>
* <li>-1<em>t1</em><em>t2</em>的左侧</li>
* <li>0<em>t1</em><em>t2</em>的重合</li>
* <li>-1<em>t1</em><em>t2</em>的右侧</li>
* </ul>
*
* @param bound 边界
* @return 位置
*/
@Override
public int compareTo(final Bound<T> bound) {
return bound instanceof NoneLowerBound ? 0 : -1;
}
/**
* 获取{@code "[value"}{@code "(value"}格式的字符串
*
* @return 字符串
*/
@Override
public String descBound() {
return getType().getSymbol() + INFINITE_MIN;
}
/**
* 对当前边界取反
*
* @return 取反后的边界
*/
@Override
public Bound<T> negate() {
return this;
}
/**
* 将当前实例转为一个区间
*
* @return 区间
*/
@Override
public BoundedRange<T> toRange() {
return BoundedRange.all();
}
/**
* 获得当前实例对应的{@code { x | x >= xxx}}格式的不等式字符串
*
* @return 字符串
*/
@Override
public String toString() {
return "{x | x > -\u221e}";
}
}

View File

@ -0,0 +1,106 @@
package cn.hutool.core.lang.range;
/**
* 无限大的右边界
*
* @param <T> 边界值类型
*/
class NoneUpperBound<T extends Comparable<? super T>> implements Bound<T> {
/**
* 无限大的右边界
*/
@SuppressWarnings("rawtypes")
static final NoneUpperBound INSTANCE = new NoneUpperBound();
private NoneUpperBound() {
}
/**
* 获取边界值
*
* @return 边界值
*/
@Override
public T getValue() {
return null;
}
/**
* 获取边界类型
*
* @return 边界类型
*/
@Override
public BoundType getType() {
return BoundType.OPEN_UPPER_BOUND;
}
/**
* 检验指定值是否在当前边界表示的范围内
*
* @param t 要检验的值不允许为{@code null}
* @return 是否
*/
@Override
public boolean test(final T t) {
return true;
}
/**
* <p>比较另一边界与当前边界在坐标轴上位置的先后顺序<br>
* 若令当前边界为<em>t1</em>另一边界为<em>t2</em>则有
* <ul>
* <li>-1<em>t1</em><em>t2</em>的左侧</li>
* <li>0<em>t1</em><em>t2</em>的重合</li>
* <li>-1<em>t1</em><em>t2</em>的右侧</li>
* </ul>
*
* @param bound 边界
* @return 位置
*/
@Override
public int compareTo(final Bound<T> bound) {
return bound instanceof NoneUpperBound ? 0 : 1;
}
/**
* 获取{@code "[value"}{@code "(value"}格式的字符串
*
* @return 字符串
*/
@Override
public String descBound() {
return INFINITE_MAX + getType().getSymbol();
}
/**
* 获得当前实例对应的{@code { x | x >= xxx}}格式的不等式字符串
*
* @return 字符串
*/
@Override
public String toString() {
return "{x | x < +\u221e}";
}
/**
* 对当前边界取反
*
* @return 取反后的边界
*/
@Override
public Bound<T> negate() {
return this;
}
/**
* 将当前实例转为一个区间
*
* @return 区间
*/
@Override
public BoundedRange<T> toRange() {
return BoundedRange.all();
}
}

View File

@ -1,5 +1,6 @@
package cn.hutool.core.lang;
package cn.hutool.core.lang.range;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.lock.NoLock;
import java.io.Serializable;

View File

@ -0,0 +1,10 @@
/**
* 提供区间和边界封装主要包括
* <ul>
* <li>{@link cn.hutool.core.lang.range.Bound}: 提供边界的抽象表示包括边界范围开闭区间等</li>
* <li>{@link cn.hutool.core.lang.range.Range}: 提供可迭代的区间</li>
* </ul>
*
* @author huangchengxing, looly
*/
package cn.hutool.core.lang.range;

View File

@ -1394,7 +1394,8 @@ public class ArrayUtil extends PrimitiveArrayUtil {
* @return 最小值
* @since 3.0.9
*/
public static <T extends Comparable<? super T>> T min(final T[] numberArray) {
@SafeVarargs
public static <T extends Comparable<? super T>> T min(final T... numberArray) {
return min(numberArray, null);
}
@ -1428,7 +1429,8 @@ public class ArrayUtil extends PrimitiveArrayUtil {
* @return 最大值
* @since 3.0.9
*/
public static <T extends Comparable<? super T>> T max(final T[] numberArray) {
@SafeVarargs
public static <T extends Comparable<? super T>> T max(final T... numberArray) {
return max(numberArray, null);
}

View File

@ -4,6 +4,7 @@ import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateRange;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.range.Range;
import cn.hutool.core.text.StrUtil;
import org.junit.Assert;
import org.junit.Test;