From 78d4a6ee1dffe8e164130363f473b39323bd769e Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 6 May 2023 02:19:40 +0800 Subject: [PATCH] fix code --- .../hutool/core/lang/WeightRandom.java | 247 ------------------ .../hutool/core/lang/selector/Selector.java | 30 +++ .../core/lang/selector/SmoothWeightObj.java | 65 +++++ .../lang/selector/SmoothWeightSelector.java | 149 +++++++++++ .../hutool/core/lang/selector/WeightObj.java | 84 ++++++ .../lang/selector/WeightRandomSelector.java | 153 +++++++++++ .../core/lang/selector/package-info.java | 21 ++ .../concurrent/ConcurrentLinkedHashMap.java | 56 ++-- .../core/map/concurrent/EntryWeigher.java | 6 +- .../hutool/core/map/concurrent/Weigher.java | 6 +- .../hutool/core/map/concurrent/Weighers.java | 56 ++-- .../hutool/core/tree/TreeNodeConfig.java | 2 +- .../dromara/hutool/core/util/RandomUtil.java | 24 +- .../hutool/core/lang/WeightRandomTest.java | 19 -- .../selector/WeightRandomSelectorTest.java | 31 +++ 15 files changed, 605 insertions(+), 344 deletions(-) delete mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/lang/WeightRandom.java create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/Selector.java create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/SmoothWeightObj.java create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/SmoothWeightSelector.java create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/WeightObj.java create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/WeightRandomSelector.java create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/package-info.java delete mode 100644 hutool-core/src/test/java/org/dromara/hutool/core/lang/WeightRandomTest.java create mode 100644 hutool-core/src/test/java/org/dromara/hutool/core/lang/selector/WeightRandomSelectorTest.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/WeightRandom.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/WeightRandom.java deleted file mode 100644 index 7c718893f..000000000 --- a/hutool-core/src/main/java/org/dromara/hutool/core/lang/WeightRandom.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) 2023 looly(loolly@aliyun.com) - * Hutool is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -package org.dromara.hutool.core.lang; - -import org.dromara.hutool.core.collection.CollUtil; -import org.dromara.hutool.core.map.MapUtil; -import org.dromara.hutool.core.util.RandomUtil; - -import java.io.Serializable; -import java.util.Random; -import java.util.SortedMap; -import java.util.TreeMap; - -/** - * 权重随机算法实现
- *

- * 平时,经常会遇到权重随机算法,从不同权重的N个元素中随机选择一个,并使得总体选择结果是按照权重分布的。如广告投放、负载均衡等。 - *

- *

- * 如有4个元素A、B、C、D,权重分别为1、2、3、4,随机结果中A:B:C:D的比例要为1:2:3:4。
- *

- * 总体思路:累加每个元素的权重A(1)-B(3)-C(6)-D(10),则4个元素的的权重管辖区间分别为[0,1)、[1,3)、[3,6)、[6,10)。
- * 然后随机出一个[0,10)之间的随机数。落在哪个区间,则该区间之后的元素即为按权重命中的元素。
- * - *

- * 参考博客:https://www.cnblogs.com/waterystone/p/5708063.html - *

- * - * @param 权重随机获取的对象类型 - * @author looly - * @since 3.3.0 - */ -public class WeightRandom implements Serializable { - private static final long serialVersionUID = -8244697995702786499L; - - private final TreeMap weightMap; - - - /** - * 创建权重随机获取器 - * - * @param 权重随机获取的对象类型 - * @return WeightRandom - */ - public static WeightRandom of() { - return new WeightRandom<>(); - } - - // ---------------------------------------------------------------------------------- Constructor start - /** - * 构造 - */ - public WeightRandom() { - weightMap = new TreeMap<>(); - - } - - /** - * 构造 - * - * @param weightObj 带有权重的对象 - */ - public WeightRandom(final WeightObj weightObj) { - this(); - if(null != weightObj) { - add(weightObj); - } - } - - /** - * 构造 - * - * @param weightObjs 带有权重的对象 - */ - public WeightRandom(final Iterable> weightObjs) { - this(); - if(CollUtil.isNotEmpty(weightObjs)) { - for (final WeightObj weightObj : weightObjs) { - add(weightObj); - } - } - } - - /** - * 构造 - * - * @param weightObjs 带有权重的对象 - */ - public WeightRandom(final WeightObj[] weightObjs) { - this(); - for (final WeightObj weightObj : weightObjs) { - add(weightObj); - } - } - // ---------------------------------------------------------------------------------- Constructor end - - /** - * 增加对象 - * - * @param obj 对象 - * @param weight 权重 - * @return this - */ - public WeightRandom add(final T obj, final double weight) { - return add(new WeightObj<>(obj, weight)); - } - - /** - * 增加对象权重 - * - * @param weightObj 权重对象 - * @return this - */ - public WeightRandom add(final WeightObj weightObj) { - if(null != weightObj) { - final double weight = weightObj.getWeight(); - if(weightObj.getWeight() > 0) { - final double lastWeight = (this.weightMap.size() == 0) ? 0 : this.weightMap.lastKey(); - this.weightMap.put(weight + lastWeight, weightObj.getObj());// 权重累加 - } - } - return this; - } - - /** - * 清空权重表 - * - * @return this - */ - public WeightRandom clear() { - if(null != this.weightMap) { - this.weightMap.clear(); - } - return this; - } - - /** - * 下一个随机对象 - * - * @return 随机对象 - */ - public T next() { - if(MapUtil.isEmpty(this.weightMap)) { - return null; - } - final Random random = RandomUtil.getRandom(); - final double randomWeight = this.weightMap.lastKey() * random.nextDouble(); - final SortedMap tailMap = this.weightMap.tailMap(randomWeight, false); - return this.weightMap.get(tailMap.firstKey()); - } - - /** - * 带有权重的对象包装 - * - * @author looly - * - * @param 对象类型 - */ - public static class WeightObj { - /** 对象 */ - private T obj; - /** 权重 */ - private final double weight; - - /** - * 构造 - * - * @param obj 对象 - * @param weight 权重 - */ - public WeightObj(final T obj, final double weight) { - this.obj = obj; - this.weight = weight; - } - - /** - * 获取对象 - * - * @return 对象 - */ - public T getObj() { - return obj; - } - - /** - * 设置对象 - * - * @param obj 对象 - */ - public void setObj(final T obj) { - this.obj = obj; - } - - /** - * 获取权重 - * - * @return 权重 - */ - public double getWeight() { - return weight; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((obj == null) ? 0 : obj.hashCode()); - final long temp; - temp = Double.doubleToLongBits(weight); - result = prime * result + (int) (temp ^ (temp >>> 32)); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final WeightObj other = (WeightObj) obj; - if (this.obj == null) { - if (other.obj != null) { - return false; - } - } else if (!this.obj.equals(other.obj)) { - return false; - } - return Double.doubleToLongBits(weight) == Double.doubleToLongBits(other.weight); - } - } - -} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/Selector.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/Selector.java new file mode 100644 index 000000000..295e2871c --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/Selector.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.lang.selector; + +/** + * 选择器接口 + * + * @param 选择对象类型 + * @author looly + * @since 6.0.0 + */ +public interface Selector { + + /** + * 选择下一个对象 + * + * @return 下一个对象 + */ + T next(); +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/SmoothWeightObj.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/SmoothWeightObj.java new file mode 100644 index 000000000..d8384008e --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/SmoothWeightObj.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.lang.selector; + +/** + * 平滑权重对象 + * + * @param 对象类型 + * @author Wind, Loolt + */ +public class SmoothWeightObj extends WeightObj { + + private int currentWeight; + + /** + * 构造 + * + * @param obj 对象 + * @param weight 权重 + */ + public SmoothWeightObj(final T obj, final int weight) { + this(obj, weight, 0); + } + + /** + * 构造 + * + * @param obj 对象 + * @param weight 权重 + * @param currentWeight 当前权重 + */ + public SmoothWeightObj(final T obj, final int weight, final int currentWeight) { + super(obj, weight); + this.currentWeight = currentWeight; + } + + /** + * 获取当前权重 + * + * @return int 临时权重 + */ + public int getCurrentWeight() { + return currentWeight; + } + + /** + * setCurrentWeight + *

设置当前权重 + * + * @param currentWeight 权重值 + */ + public void setCurrentWeight(final int currentWeight) { + this.currentWeight = currentWeight; + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/SmoothWeightSelector.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/SmoothWeightSelector.java new file mode 100644 index 000000000..bb7933341 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/SmoothWeightSelector.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.lang.selector; + +import org.dromara.hutool.core.collection.CollUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * 平滑加权轮询选择器 + * + *

+ * 来自:https://gitee.com/dromara/hutool/pulls/982/ + *

+ *

+ * 介绍:https://cloud.tencent.com/developer/beta/article/1680928 + *

+ * + *

+ * 思路: 比如 A : 5 , B : 3 , C : 2 (服务器 A,B,C 对应权重分别是 5,3,2) + * ip: A,B,C + * weight: 5,3,2 (计算得到 totalWeight = 10) + * currentWeight: 0,0,0 (当前ip的初始权重都为0) + *

+ * 请求次数: |  currentWeight = currentWeight + weight  |  最大权重为  |  返回的ip为 |  最大的权重 - totalWeight,其余不变
+ *      1   |           5,3,2    (0,0,0 + 5,3,2)       |     5      |      A     |      -5,3,2
+ *      2   |           0,6,4    (-5,3,2 + 5,3,2)      |     6      |      B     |       0,-4,4
+ *      3   |           5,-1,6    (0,-4,4 + 5,3,2)     |     6      |     C      |       5,-1,-4
+ *      4   |          10,2,-2    (5,-1,-4 + 5,3,2)    |     10     |     A      |       0,2,-2
+ *      5   |           5,5,0                          |     5      |     A      |       -5,5,0
+ *      6   |           0,8,2                          |     8      |     B      |       0,-2,2
+ *      7   |           5,1,4                          |     5      |     A      |       -5,1,4
+ *      8   |           0,4,6                          |     6      |     C      |       0,4,-4
+ *      9   |           5,7,-2                         |     7      |     B      |       5,-3,-2
+ *      10  |           10,0,0                         |     10     |     A      |        0,0,0
+ * 
+ * + * 至此结束: 可以看到负载轮询的策略是: A,B,C,A,A,B,A,C,B,A, + * + * @param 对象类型 + * @author :Wind, Looly + */ +public class SmoothWeightSelector implements Selector { + + /** + * 创建平滑加权获取器 + * + * @param 对象类型 + * @return SmoothSelector + */ + public static SmoothWeightSelector of() { + return new SmoothWeightSelector<>(); + } + + private final List> objList; + + // region ----- Constructors + + /** + * 构造 + */ + public SmoothWeightSelector() { + this.objList = new ArrayList<>(); + } + + /** + * 构造 + * + * @param weightObjList 权重对象列表 + */ + public SmoothWeightSelector(final Iterable> weightObjList) { + this(); + for (final WeightObj weightObj : weightObjList) { + add(weightObj); + } + } + // endregion + + /** + * 增加对象 + * + * @param obj 对象 + * @param weight 权重 + * @return this + */ + public SmoothWeightSelector add(final T obj, final int weight) { + return add(new SmoothWeightObj<>(obj, weight)); + } + + /** + * 增加权重对象 + * + * @param weightObj 权重对象 + * @return this + */ + public SmoothWeightSelector add(final WeightObj weightObj) { + final SmoothWeightObj smoothWeightObj; + if (weightObj instanceof SmoothWeightObj) { + smoothWeightObj = (SmoothWeightObj) weightObj; + } else { + smoothWeightObj = new SmoothWeightObj<>(weightObj.obj, weightObj.weight); + } + this.objList.add(smoothWeightObj); + return this; + } + + /** + * 通过平滑加权方法获取列表中的当前对象 + * + * @return 选中的对象 + */ + @Override + public T next() { + if (CollUtil.isEmpty(this.objList)) { + return null; + } + int totalWeight = 0; + SmoothWeightObj selected = null; + + for (final SmoothWeightObj obj : objList) { + totalWeight += obj.getWeight(); + final int currentWeight = obj.getCurrentWeight() + obj.getWeight(); + obj.setCurrentWeight(currentWeight); + if (null == selected || currentWeight > selected.getCurrentWeight()) { + selected = obj; + } + } + + if (null == selected) { + return null; + } + + // 更新选择的对象的当前权重,并返回其地址 + selected.setCurrentWeight(selected.getCurrentWeight() - totalWeight); + + return selected.getObj(); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/WeightObj.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/WeightObj.java new file mode 100644 index 000000000..0a4bf840d --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/WeightObj.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.lang.selector; + +import java.util.Objects; + +/** + * 带有权重的对象包装 + * + * @author looly + * + * @param 对象类型 + */ +public class WeightObj { + /** 对象 */ + protected T obj; + /** 权重 */ + protected final int weight; + + /** + * 构造 + * + * @param obj 对象 + * @param weight 权重 + */ + public WeightObj(final T obj, final int weight) { + this.obj = obj; + this.weight = weight; + } + + /** + * 获取对象 + * + * @return 对象 + */ + public T getObj() { + return obj; + } + + /** + * 设置对象 + * + * @param obj 对象 + */ + public void setObj(final T obj) { + this.obj = obj; + } + + /** + * 获取权重 + * + * @return 权重 + */ + public int getWeight() { + return weight; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final WeightObj weightObj = (WeightObj) o; + return weight == weightObj.weight && Objects.equals(obj, weightObj.obj); + } + + @Override + public int hashCode() { + return Objects.hash(obj, weight); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/WeightRandomSelector.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/WeightRandomSelector.java new file mode 100644 index 000000000..215918b00 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/WeightRandomSelector.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.lang.selector; + +import org.dromara.hutool.core.collection.CollUtil; + +import java.io.Serializable; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * 权重随机选择算法实现
+ *

+ * 平时,经常会遇到权重随机算法,从不同权重的N个元素中随机选择一个,并使得总体选择结果是按照权重分布的。如广告投放、负载均衡等。 + *

+ *

+ * 如有4个元素A、B、C、D,权重分别为1、2、3、4,随机结果中A:B:C:D的比例要为1:2:3:4。
+ *

+ * 总体思路:累加每个元素的权重A(1)-B(3)-C(6)-D(10),则4个元素的的权重管辖区间分别为[0,1)、[1,3)、[3,6)、[6,10)。
+ * 然后随机出一个[0,10)之间的随机数。落在哪个区间,则该区间之后的元素即为按权重命中的元素。
+ * + *

+ * 参考博客:https://www.cnblogs.com/waterystone/p/5708063.html + * + * @param 权重随机获取的对象类型 + * @author looly + * @since 3.3.0 + */ +public class WeightRandomSelector implements Selector, Serializable { + private static final long serialVersionUID = -8244697995702786499L; + + /** + * 创建权重随机获取器 + * + * @param 权重随机获取的对象类型 + * @return WeightRandomSelector + */ + public static WeightRandomSelector of() { + return new WeightRandomSelector<>(); + } + + private final TreeMap weightMap; + + // region ----- Constructors + + /** + * 构造 + */ + public WeightRandomSelector() { + weightMap = new TreeMap<>(); + } + + /** + * 构造 + * + * @param weightObj 带有权重的对象 + */ + public WeightRandomSelector(final WeightObj weightObj) { + this(); + if (null != weightObj) { + add(weightObj); + } + } + + /** + * 构造 + * + * @param weightObjs 带有权重的对象 + */ + public WeightRandomSelector(final Iterable> weightObjs) { + this(); + if (CollUtil.isNotEmpty(weightObjs)) { + for (final WeightObj weightObj : weightObjs) { + add(weightObj); + } + } + } + + /** + * 构造 + * + * @param weightObjs 带有权重的对象 + */ + public WeightRandomSelector(final WeightObj[] weightObjs) { + this(); + for (final WeightObj weightObj : weightObjs) { + add(weightObj); + } + } + // endregion + + /** + * 增加对象 + * + * @param obj 对象 + * @param weight 权重 + * @return this + */ + public WeightRandomSelector add(final T obj, final int weight) { + return add(new WeightObj<>(obj, weight)); + } + + /** + * 增加对象权重 + * + * @param weightObj 权重对象 + * @return this + */ + public WeightRandomSelector add(final WeightObj weightObj) { + if (null != weightObj) { + final int weight = weightObj.getWeight(); + if (weight > 0) { + final int lastWeight = this.weightMap.isEmpty() ? 0 : this.weightMap.lastKey(); + this.weightMap.put(weight + lastWeight, weightObj.getObj());// 权重累加 + } + } + return this; + } + + /** + * 清空权重表 + * + * @return this + */ + public WeightRandomSelector clear() { + if (null != this.weightMap) { + this.weightMap.clear(); + } + return this; + } + + /** + * 下一个随机对象 + * + * @return 随机对象 + */ + @Override + public T next() { + final int randomWeight = (int) (this.weightMap.lastKey() * Math.random()); + final SortedMap tailMap = this.weightMap.tailMap(randomWeight, false); + return this.weightMap.get(tailMap.firstKey()); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/package-info.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/package-info.java new file mode 100644 index 000000000..b15cd29df --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/selector/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/** + * 选择器相关封装,包括: + *

    + *
  • {@link org.dromara.hutool.core.lang.selector.WeightRandomSelector}
  • + *
+ * + * @author looly + */ +package org.dromara.hutool.core.lang.selector; diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/ConcurrentLinkedHashMap.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/ConcurrentLinkedHashMap.java index 71df73fd4..fa42d9f8b 100755 --- a/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/ConcurrentLinkedHashMap.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/ConcurrentLinkedHashMap.java @@ -40,18 +40,18 @@ import java.util.function.BiConsumer; * concurrency for updates, and a maximum capacity to bound the map by. This * implementation differs from {@link ConcurrentHashMap} in that it maintains a * page replacement algorithm that is used to evict an entry when the map has - * exceeded its capacity. Unlike the Java Collections Framework, this + * exceeded its capacity. Unlike the {@code Java Collections Framework}, this * map does not have a publicly visible constructor and instances are created * through a {@link Builder}. *

- * An entry is evicted from the map when the weighted capacity exceeds - * its maximum weighted capacity threshold. A {@link EntryWeigher} + * An entry is evicted from the map when the {@code weighted capacity} exceeds + * its {@code maximum weighted capacity} threshold. A {@link EntryWeigher} * determines how many units of capacity that an entry consumes. The default - * weigher assigns each value a weight of 1 to bound the map by the + * weigher assigns each value a selector of {@code 1} to bound the map by the * total number of key-value pairs. A map that holds collections may choose to * weigh values by the number of elements in the collection and bound the map * by the total number of elements that it contains. A change to a value that - * modifies its weight requires that an update operation is performed on the + * modifies its selector requires that an update operation is performed on the * map. *

* An {@link BiConsumer} may be supplied for notification when an entry @@ -63,7 +63,7 @@ import java.util.function.BiConsumer; * operation asynchronously, such as by submitting a task to an * {@link java.util.concurrent.ExecutorService}. *

- * The concurrency level determines the number of threads that can + * The {@code concurrency level} determines the number of threads that can * concurrently modify the table. Using a significantly higher or lower value * than needed can waste space or lead to thread contention, but an estimate * within an order of magnitude of the ideal value does not usually have a @@ -75,7 +75,7 @@ import java.util.function.BiConsumer; * interfaces. *

* Like {@link Hashtable} but unlike {@link HashMap}, this class - * does not allow null to be used as a key or value. Unlike + * does not allow {@code null} to be used as a key or value. Unlike * {@link LinkedHashMap}, this class does not provide * predictable iteration order. A snapshot of the keys and entries may be * obtained in ascending and descending order of retention. @@ -112,16 +112,16 @@ public final class ConcurrentLinkedHashMap extends AbstractMap * * Due to a lack of a strict ordering guarantee, a task can be executed * out-of-order, such as a removal followed by its addition. The state of the - * entry is encoded within the value's weight. + * entry is encoded within the value's selector. * * Alive: The entry is in both the hash-table and the page replacement policy. - * This is represented by a positive weight. + * This is represented by a positive selector. * * Retired: The entry is not in the hash-table and is pending removal from the - * page replacement policy. This is represented by a negative weight. + * page replacement policy. This is represented by a negative selector. * * Dead: The entry is not in the hash-table and is not in the page replacement - * policy. This is represented by a weight of zero. + * policy. This is represented by a selector of zero. * * The Least Recently Used page replacement algorithm was chosen due to its * simplicity, high hit rate, and ability to be implemented with O(1) time @@ -297,7 +297,7 @@ public final class ConcurrentLinkedHashMap extends AbstractMap final Node node = evictionDeque.poll(); // If weighted values are used, then the pending operations will adjust - // the size to reflect the correct weight + // the size to reflect the correct selector if (node == null) { return; } @@ -466,8 +466,8 @@ public final class ConcurrentLinkedHashMap extends AbstractMap } /** - * Attempts to transition the node from the alive state to the - * retired state. + * Attempts to transition the node from the {@code alive} state to the + * {@code retired} state. * * @param node the entry in the page replacement policy * @param expect the expected weighted value @@ -482,8 +482,8 @@ public final class ConcurrentLinkedHashMap extends AbstractMap } /** - * Atomically transitions the node from the alive state to the - * retired state, if a valid transition. + * Atomically transitions the node from the {@code alive} state to the + * {@code retired} state, if a valid transition. * * @param node the entry in the page replacement policy */ @@ -501,8 +501,8 @@ public final class ConcurrentLinkedHashMap extends AbstractMap } /** - * Atomically transitions the node to the dead state and decrements - * the weightedSize. + * Atomically transitions the node to the {@code dead} state and decrements + * the {@code weightedSize}. * * @param node the entry in the page replacement policy */ @@ -608,7 +608,7 @@ public final class ConcurrentLinkedHashMap extends AbstractMap /** * Returns the weighted size of this map. * - * @return the combined weight of the values in this map + * @return the combined selector of the values in this map */ public long weightedSize() { return Math.max(0, weightedSize.get()); @@ -1097,7 +1097,7 @@ public final class ConcurrentLinkedHashMap extends AbstractMap } /** - * A value, its weight, and the entry's status. + * A value, its selector, and the entry's status. */ static final class WeightedValue { final int weight; @@ -1178,7 +1178,7 @@ public final class ConcurrentLinkedHashMap extends AbstractMap } /** - * Retrieves the value held by the current WeightedValue. + * Retrieves the value held by the current {@code WeightedValue}. */ V getValue() { return get().value; @@ -1401,7 +1401,7 @@ public final class ConcurrentLinkedHashMap extends AbstractMap } /** - * A weigher that enforces that the weight falls within a valid range. + * A weigher that enforces that the selector falls within a valid range. */ static final class BoundedEntryWeigher implements EntryWeigher, Serializable { private static final long serialVersionUID = 1; @@ -1520,7 +1520,7 @@ public final class ConcurrentLinkedHashMap extends AbstractMap } /** - * Specifies the initial capacity of the hash table (default 16). + * Specifies the initial capacity of the hash table (default {@code 16}). * This is the number of key-value pairs that the hash table can hold * before a resize operation is required. * @@ -1553,7 +1553,7 @@ public final class ConcurrentLinkedHashMap extends AbstractMap /** * Specifies the estimated number of concurrently updating threads. The * implementation performs internal sizing to try to accommodate this many - * threads (default 16). + * threads (default {@code 16}). * * @param concurrencyLevel the estimated number of concurrently updating * threads @@ -1584,9 +1584,9 @@ public final class ConcurrentLinkedHashMap extends AbstractMap /** * Specifies an algorithm to determine how many the units of capacity a * value consumes. The default algorithm bounds the map by the number of - * key-value pairs by giving each entry a weight of 1. + * key-value pairs by giving each entry a selector of {@code 1}. * - * @param weigher the algorithm to determine a value's weight + * @param weigher the algorithm to determine a value's selector * @return this * @throws NullPointerException if the weigher is null */ @@ -1600,9 +1600,9 @@ public final class ConcurrentLinkedHashMap extends AbstractMap /** * Specifies an algorithm to determine how many the units of capacity an * entry consumes. The default algorithm bounds the map by the number of - * key-value pairs by giving each entry a weight of 1. + * key-value pairs by giving each entry a selector of {@code 1}. * - * @param weigher the algorithm to determine a entry's weight + * @param weigher the algorithm to determine a entry's selector * @return this * @throws NullPointerException if the weigher is null */ diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/EntryWeigher.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/EntryWeigher.java index 18d6eab2d..14f78699e 100755 --- a/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/EntryWeigher.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/EntryWeigher.java @@ -16,7 +16,7 @@ package org.dromara.hutool.core.map.concurrent; /** - * A class that can determine the weight of an entry. The total weight threshold + * A class that can determine the selector of an entry. The total selector threshold * is used to determine when an eviction is required. * * @param 键类型 @@ -28,12 +28,12 @@ package org.dromara.hutool.core.map.concurrent; public interface EntryWeigher { /** - * Measures an entry's weight to determine how many units of capacity that + * Measures an entry's selector to determine how many units of capacity that * the key and value consumes. An entry must consume a minimum of one unit. * * @param key the key to weigh * @param value the value to weigh - * @return the entry's weight + * @return the entry's selector */ int weightOf(K key, V value); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/Weigher.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/Weigher.java index d2b28e3e6..bd54810f7 100755 --- a/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/Weigher.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/Weigher.java @@ -16,7 +16,7 @@ package org.dromara.hutool.core.map.concurrent; /** - * A class that can determine the weight of a value. The total weight threshold + * A class that can determine the selector of a value. The total selector threshold * is used to determine when an eviction is required. * * @param 值类型 @@ -27,11 +27,11 @@ package org.dromara.hutool.core.map.concurrent; public interface Weigher { /** - * Measures an object's weight to determine how many units of capacity that + * Measures an object's selector to determine how many units of capacity that * the value consumes. A value must consume a minimum of one unit. * * @param value the object to weigh - * @return the object's weight + * @return the object's selector */ int weightOf(V value); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/Weighers.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/Weighers.java index 45ad245b9..0949b586a 100755 --- a/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/Weighers.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/concurrent/Weighers.java @@ -38,8 +38,8 @@ public final class Weighers { } /** - * A entry weigher backed by the specified weigher. The weight of the value - * determines the weight of the entry. + * A entry weigher backed by the specified weigher. The selector of the value + * determines the selector of the entry. * * @param weigher the weigher to be "wrapped" in a entry weigher. * @param 键类型 @@ -54,7 +54,7 @@ public final class Weighers { } /** - * A weigher where an entry has a weight of 1. A map bounded with + * A weigher where an entry has a selector of 1. A map bounded with * this weigher will evict when the number of key-value pairs exceeds the * capacity. * @@ -68,7 +68,7 @@ public final class Weighers { } /** - * A weigher where a value has a weight of 1. A map bounded with + * A weigher where a value has a selector of 1. A map bounded with * this weigher will evict when the number of key-value pairs exceeds the * capacity. * @@ -81,17 +81,17 @@ public final class Weighers { } /** - * A weigher where the value is a byte array and its weight is the number of + * A weigher where the value is a byte array and its selector is the number of * bytes. A map bounded with this weigher will evict when the number of bytes * exceeds the capacity rather than the number of key-value pairs in the map. * This allows for restricting the capacity based on the memory-consumption * and is primarily for usage by dedicated caching servers that hold the * serialized data. *

- * A value with a weight of 0 will be rejected by the map. If a value - * with this weight can occur then the caller should eagerly evaluate the + * A value with a selector of 0 will be rejected by the map. If a value + * with this selector can occur then the caller should eagerly evaluate the * value and treat it as a removal operation. Alternatively, a custom weigher - * may be specified on the map to assign an empty value a positive weight. + * may be specified on the map to assign an empty value a positive selector. * * @return A weigher where each byte takes one unit of capacity. */ @@ -100,16 +100,16 @@ public final class Weighers { } /** - * A weigher where the value is a {@link Iterable} and its weight is the + * A weigher where the value is a {@link Iterable} and its selector is the * number of elements. This weigher only should be used when the alternative * {@link #collection()} weigher cannot be, as evaluation takes O(n) time. A * map bounded with this weigher will evict when the total number of elements * exceeds the capacity rather than the number of key-value pairs in the map. *

- * A value with a weight of 0 will be rejected by the map. If a value - * with this weight can occur then the caller should eagerly evaluate the + * A value with a selector of 0 will be rejected by the map. If a value + * with this selector can occur then the caller should eagerly evaluate the * value and treat it as a removal operation. Alternatively, a custom weigher - * may be specified on the map to assign an empty value a positive weight. + * may be specified on the map to assign an empty value a positive selector. * * @param 元素类型 * @return A weigher where each element takes one unit of capacity. @@ -120,15 +120,15 @@ public final class Weighers { } /** - * A weigher where the value is a {@link Collection} and its weight is the + * A weigher where the value is a {@link Collection} and its selector is the * number of elements. A map bounded with this weigher will evict when the * total number of elements exceeds the capacity rather than the number of * key-value pairs in the map. *

- * A value with a weight of 0 will be rejected by the map. If a value - * with this weight can occur then the caller should eagerly evaluate the + * A value with a selector of 0 will be rejected by the map. If a value + * with this selector can occur then the caller should eagerly evaluate the * value and treat it as a removal operation. Alternatively, a custom weigher - * may be specified on the map to assign an empty value a positive weight. + * may be specified on the map to assign an empty value a positive selector. * * @param 元素类型 * @return A weigher where each element takes one unit of capacity. @@ -139,15 +139,15 @@ public final class Weighers { } /** - * A weigher where the value is a {@link List} and its weight is the number + * A weigher where the value is a {@link List} and its selector is the number * of elements. A map bounded with this weigher will evict when the total * number of elements exceeds the capacity rather than the number of * key-value pairs in the map. *

- * A value with a weight of 0 will be rejected by the map. If a value - * with this weight can occur then the caller should eagerly evaluate the + * A value with a selector of 0 will be rejected by the map. If a value + * with this selector can occur then the caller should eagerly evaluate the * value and treat it as a removal operation. Alternatively, a custom weigher - * may be specified on the map to assign an empty value a positive weight. + * may be specified on the map to assign an empty value a positive selector. * * @param 元素类型 * @return A weigher where each element takes one unit of capacity. @@ -158,15 +158,15 @@ public final class Weighers { } /** - * A weigher where the value is a {@link Set} and its weight is the number + * A weigher where the value is a {@link Set} and its selector is the number * of elements. A map bounded with this weigher will evict when the total * number of elements exceeds the capacity rather than the number of * key-value pairs in the map. *

- * A value with a weight of 0 will be rejected by the map. If a value - * with this weight can occur then the caller should eagerly evaluate the + * A value with a selector of 0 will be rejected by the map. If a value + * with this selector can occur then the caller should eagerly evaluate the * value and treat it as a removal operation. Alternatively, a custom weigher - * may be specified on the map to assign an empty value a positive weight. + * may be specified on the map to assign an empty value a positive selector. * * @param 元素类型 * @return A weigher where each element takes one unit of capacity. @@ -177,15 +177,15 @@ public final class Weighers { } /** - * A weigher where the value is a {@link Map} and its weight is the number of + * A weigher where the value is a {@link Map} and its selector is the number of * entries. A map bounded with this weigher will evict when the total number of * entries across all values exceeds the capacity rather than the number of * key-value pairs in the map. *

- * A value with a weight of 0 will be rejected by the map. If a value - * with this weight can occur then the caller should eagerly evaluate the + * A value with a selector of 0 will be rejected by the map. If a value + * with this selector can occur then the caller should eagerly evaluate the * value and treat it as a removal operation. Alternatively, a custom weigher - * may be specified on the map to assign an empty value a positive weight. + * may be specified on the map to assign an empty value a positive selector. * * @param 键类型 * @param 值类型 diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/tree/TreeNodeConfig.java b/hutool-core/src/main/java/org/dromara/hutool/core/tree/TreeNodeConfig.java index aa100ed78..04dc9dc41 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/tree/TreeNodeConfig.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/tree/TreeNodeConfig.java @@ -30,7 +30,7 @@ public class TreeNodeConfig implements Serializable { // 属性名配置字段 private String idKey = "id"; private String parentIdKey = "parentId"; - private String weightKey = "weight"; + private String weightKey = "selector"; private String nameKey = "name"; private String childrenKey = "children"; // 可以配置递归深度 从0开始计算 默认此配置为空,即不限制 diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java index 9d2f6fa89..6031cd469 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java @@ -20,8 +20,8 @@ import org.dromara.hutool.core.date.DateTime; import org.dromara.hutool.core.date.DateUtil; import org.dromara.hutool.core.exception.HutoolException; import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.core.lang.WeightRandom; -import org.dromara.hutool.core.lang.WeightRandom.WeightObj; +import org.dromara.hutool.core.lang.selector.WeightObj; +import org.dromara.hutool.core.lang.selector.WeightRandomSelector; import org.dromara.hutool.core.math.NumberUtil; import org.dromara.hutool.core.text.StrUtil; @@ -29,13 +29,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Random; -import java.util.Set; +import java.util.*; import java.util.concurrent.ThreadLocalRandom; /** @@ -637,11 +631,11 @@ public class RandomUtil { * * @param 随机对象类型 * @param weightObjs 带有权重的对象列表 - * @return {@link WeightRandom} + * @return {@link WeightRandomSelector} * @since 4.0.3 */ - public static WeightRandom weightRandom(final WeightObj[] weightObjs) { - return new WeightRandom<>(weightObjs); + public static WeightRandomSelector weightRandom(final WeightObj[] weightObjs) { + return new WeightRandomSelector<>(weightObjs); } /** @@ -649,11 +643,11 @@ public class RandomUtil { * * @param 随机对象类型 * @param weightObjs 带有权重的对象列表 - * @return {@link WeightRandom} + * @return {@link WeightRandomSelector} * @since 4.0.3 */ - public static WeightRandom weightRandom(final Iterable> weightObjs) { - return new WeightRandom<>(weightObjs); + public static WeightRandomSelector weightRandom(final Iterable> weightObjs) { + return new WeightRandomSelector<>(weightObjs); } /** diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/lang/WeightRandomTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/lang/WeightRandomTest.java deleted file mode 100644 index 9ce168f1f..000000000 --- a/hutool-core/src/test/java/org/dromara/hutool/core/lang/WeightRandomTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.dromara.hutool.core.lang; - -import org.dromara.hutool.core.collection.ListUtil; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class WeightRandomTest { - - @Test - public void weightRandomTest() { - final WeightRandom random = WeightRandom.of(); - random.add("A", 10); - random.add("B", 50); - random.add("C", 100); - - final String result = random.next(); - Assertions.assertTrue(ListUtil.of("A", "B", "C").contains(result)); - } -} diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/lang/selector/WeightRandomSelectorTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/lang/selector/WeightRandomSelectorTest.java new file mode 100644 index 000000000..3ad880540 --- /dev/null +++ b/hutool-core/src/test/java/org/dromara/hutool/core/lang/selector/WeightRandomSelectorTest.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.lang.selector; + +import org.dromara.hutool.core.collection.ListUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class WeightRandomSelectorTest { + + @Test + public void weightRandomTest() { + final WeightRandomSelector random = WeightRandomSelector.of(); + random.add("A", 10); + random.add("B", 50); + random.add("C", 100); + + final String result = random.next(); + Assertions.assertTrue(ListUtil.of("A", "B", "C").contains(result)); + } +}