mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
fix code
This commit is contained in:
parent
7db850fe62
commit
78d4a6ee1d
@ -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;
|
||||
|
||||
/**
|
||||
* 权重随机算法实现<br>
|
||||
* <p>
|
||||
* 平时,经常会遇到权重随机算法,从不同权重的N个元素中随机选择一个,并使得总体选择结果是按照权重分布的。如广告投放、负载均衡等。
|
||||
* </p>
|
||||
* <p>
|
||||
* 如有4个元素A、B、C、D,权重分别为1、2、3、4,随机结果中A:B:C:D的比例要为1:2:3:4。<br>
|
||||
* </p>
|
||||
* 总体思路:累加每个元素的权重A(1)-B(3)-C(6)-D(10),则4个元素的的权重管辖区间分别为[0,1)、[1,3)、[3,6)、[6,10)。<br>
|
||||
* 然后随机出一个[0,10)之间的随机数。落在哪个区间,则该区间之后的元素即为按权重命中的元素。<br>
|
||||
*
|
||||
* <p>
|
||||
* 参考博客:<a href="https://www.cnblogs.com/waterystone/p/5708063.html">https://www.cnblogs.com/waterystone/p/5708063.html</a>
|
||||
* <p>
|
||||
*
|
||||
* @param <T> 权重随机获取的对象类型
|
||||
* @author looly
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public class WeightRandom<T> implements Serializable {
|
||||
private static final long serialVersionUID = -8244697995702786499L;
|
||||
|
||||
private final TreeMap<Double, T> weightMap;
|
||||
|
||||
|
||||
/**
|
||||
* 创建权重随机获取器
|
||||
*
|
||||
* @param <T> 权重随机获取的对象类型
|
||||
* @return WeightRandom
|
||||
*/
|
||||
public static <T> WeightRandom<T> of() {
|
||||
return new WeightRandom<>();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------- Constructor start
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public WeightRandom() {
|
||||
weightMap = new TreeMap<>();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param weightObj 带有权重的对象
|
||||
*/
|
||||
public WeightRandom(final WeightObj<T> weightObj) {
|
||||
this();
|
||||
if(null != weightObj) {
|
||||
add(weightObj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param weightObjs 带有权重的对象
|
||||
*/
|
||||
public WeightRandom(final Iterable<WeightObj<T>> weightObjs) {
|
||||
this();
|
||||
if(CollUtil.isNotEmpty(weightObjs)) {
|
||||
for (final WeightObj<T> weightObj : weightObjs) {
|
||||
add(weightObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param weightObjs 带有权重的对象
|
||||
*/
|
||||
public WeightRandom(final WeightObj<T>[] weightObjs) {
|
||||
this();
|
||||
for (final WeightObj<T> weightObj : weightObjs) {
|
||||
add(weightObj);
|
||||
}
|
||||
}
|
||||
// ---------------------------------------------------------------------------------- Constructor end
|
||||
|
||||
/**
|
||||
* 增加对象
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param weight 权重
|
||||
* @return this
|
||||
*/
|
||||
public WeightRandom<T> add(final T obj, final double weight) {
|
||||
return add(new WeightObj<>(obj, weight));
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加对象权重
|
||||
*
|
||||
* @param weightObj 权重对象
|
||||
* @return this
|
||||
*/
|
||||
public WeightRandom<T> add(final WeightObj<T> 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<T> 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<Double, T> tailMap = this.weightMap.tailMap(randomWeight, false);
|
||||
return this.weightMap.get(tailMap.firstKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 带有权重的对象包装
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
* @param <T> 对象类型
|
||||
*/
|
||||
public static class WeightObj<T> {
|
||||
/** 对象 */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 <T> 选择对象类型
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface Selector<T> {
|
||||
|
||||
/**
|
||||
* 选择下一个对象
|
||||
*
|
||||
* @return 下一个对象
|
||||
*/
|
||||
T next();
|
||||
}
|
@ -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 <T> 对象类型
|
||||
* @author Wind, Loolt
|
||||
*/
|
||||
public class SmoothWeightObj<T> extends WeightObj<T> {
|
||||
|
||||
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
|
||||
* <p>设置当前权重
|
||||
*
|
||||
* @param currentWeight 权重值
|
||||
*/
|
||||
public void setCurrentWeight(final int currentWeight) {
|
||||
this.currentWeight = currentWeight;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
/**
|
||||
* 平滑加权轮询选择器
|
||||
*
|
||||
* <p>
|
||||
* 来自:https://gitee.com/dromara/hutool/pulls/982/
|
||||
* </p>
|
||||
* <p>
|
||||
* 介绍:https://cloud.tencent.com/developer/beta/article/1680928
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 思路: 比如 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)
|
||||
* <pre>
|
||||
* 请求次数: | 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
|
||||
* </pre>
|
||||
*
|
||||
* 至此结束: 可以看到负载轮询的策略是: A,B,C,A,A,B,A,C,B,A,
|
||||
*
|
||||
* @param <T> 对象类型
|
||||
* @author :Wind, Looly
|
||||
*/
|
||||
public class SmoothWeightSelector<T> implements Selector<T> {
|
||||
|
||||
/**
|
||||
* 创建平滑加权获取器
|
||||
*
|
||||
* @param <T> 对象类型
|
||||
* @return SmoothSelector
|
||||
*/
|
||||
public static <T> SmoothWeightSelector<T> of() {
|
||||
return new SmoothWeightSelector<>();
|
||||
}
|
||||
|
||||
private final List<SmoothWeightObj<T>> objList;
|
||||
|
||||
// region ----- Constructors
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public SmoothWeightSelector() {
|
||||
this.objList = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param weightObjList 权重对象列表
|
||||
*/
|
||||
public SmoothWeightSelector(final Iterable<? extends WeightObj<T>> weightObjList) {
|
||||
this();
|
||||
for (final WeightObj<T> weightObj : weightObjList) {
|
||||
add(weightObj);
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 增加对象
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param weight 权重
|
||||
* @return this
|
||||
*/
|
||||
public SmoothWeightSelector<T> add(final T obj, final int weight) {
|
||||
return add(new SmoothWeightObj<>(obj, weight));
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加权重对象
|
||||
*
|
||||
* @param weightObj 权重对象
|
||||
* @return this
|
||||
*/
|
||||
public SmoothWeightSelector<T> add(final WeightObj<T> weightObj) {
|
||||
final SmoothWeightObj<T> smoothWeightObj;
|
||||
if (weightObj instanceof SmoothWeightObj) {
|
||||
smoothWeightObj = (SmoothWeightObj<T>) 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<T> selected = null;
|
||||
|
||||
for (final SmoothWeightObj<T> 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();
|
||||
}
|
||||
}
|
@ -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 <T> 对象类型
|
||||
*/
|
||||
public class WeightObj<T> {
|
||||
/** 对象 */
|
||||
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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
/**
|
||||
* 权重随机选择算法实现<br>
|
||||
* <p>
|
||||
* 平时,经常会遇到权重随机算法,从不同权重的N个元素中随机选择一个,并使得总体选择结果是按照权重分布的。如广告投放、负载均衡等。
|
||||
* </p>
|
||||
* <p>
|
||||
* 如有4个元素A、B、C、D,权重分别为1、2、3、4,随机结果中A:B:C:D的比例要为1:2:3:4。<br>
|
||||
* </p>
|
||||
* 总体思路:累加每个元素的权重A(1)-B(3)-C(6)-D(10),则4个元素的的权重管辖区间分别为[0,1)、[1,3)、[3,6)、[6,10)。<br>
|
||||
* 然后随机出一个[0,10)之间的随机数。落在哪个区间,则该区间之后的元素即为按权重命中的元素。<br>
|
||||
*
|
||||
* <p>
|
||||
* 参考博客:https://www.cnblogs.com/waterystone/p/5708063.html
|
||||
*
|
||||
* @param <T> 权重随机获取的对象类型
|
||||
* @author looly
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public class WeightRandomSelector<T> implements Selector<T>, Serializable {
|
||||
private static final long serialVersionUID = -8244697995702786499L;
|
||||
|
||||
/**
|
||||
* 创建权重随机获取器
|
||||
*
|
||||
* @param <T> 权重随机获取的对象类型
|
||||
* @return WeightRandomSelector
|
||||
*/
|
||||
public static <T> WeightRandomSelector<T> of() {
|
||||
return new WeightRandomSelector<>();
|
||||
}
|
||||
|
||||
private final TreeMap<Integer, T> weightMap;
|
||||
|
||||
// region ----- Constructors
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public WeightRandomSelector() {
|
||||
weightMap = new TreeMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param weightObj 带有权重的对象
|
||||
*/
|
||||
public WeightRandomSelector(final WeightObj<T> weightObj) {
|
||||
this();
|
||||
if (null != weightObj) {
|
||||
add(weightObj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param weightObjs 带有权重的对象
|
||||
*/
|
||||
public WeightRandomSelector(final Iterable<WeightObj<T>> weightObjs) {
|
||||
this();
|
||||
if (CollUtil.isNotEmpty(weightObjs)) {
|
||||
for (final WeightObj<T> weightObj : weightObjs) {
|
||||
add(weightObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param weightObjs 带有权重的对象
|
||||
*/
|
||||
public WeightRandomSelector(final WeightObj<T>[] weightObjs) {
|
||||
this();
|
||||
for (final WeightObj<T> weightObj : weightObjs) {
|
||||
add(weightObj);
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 增加对象
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param weight 权重
|
||||
* @return this
|
||||
*/
|
||||
public WeightRandomSelector<T> add(final T obj, final int weight) {
|
||||
return add(new WeightObj<>(obj, weight));
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加对象权重
|
||||
*
|
||||
* @param weightObj 权重对象
|
||||
* @return this
|
||||
*/
|
||||
public WeightRandomSelector<T> add(final WeightObj<T> 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<T> 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<Integer, T> tailMap = this.weightMap.tailMap(randomWeight, false);
|
||||
return this.weightMap.get(tailMap.firstKey());
|
||||
}
|
||||
}
|
@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 选择器相关封装,包括:
|
||||
* <ul>
|
||||
* <li>{@link org.dromara.hutool.core.lang.selector.WeightRandomSelector}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package org.dromara.hutool.core.lang.selector;
|
@ -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 <tt>Java Collections Framework</tt>, 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}.
|
||||
* <p>
|
||||
* An entry is evicted from the map when the <tt>weighted capacity</tt> exceeds
|
||||
* its <tt>maximum weighted capacity</tt> 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 <tt>1</tt> 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.
|
||||
* <p>
|
||||
* 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}.
|
||||
* <p>
|
||||
* The <tt>concurrency level</tt> 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.
|
||||
* <p>
|
||||
* Like {@link Hashtable} but unlike {@link HashMap}, this class
|
||||
* does <em>not</em> allow <tt>null</tt> to be used as a key or value. Unlike
|
||||
* does <em>not</em> allow {@code null} to be used as a key or value. Unlike
|
||||
* {@link LinkedHashMap}, this class does <em>not</em> 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<K, V> extends AbstractMap<K, V>
|
||||
*
|
||||
* 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<K, V> extends AbstractMap<K, V>
|
||||
final Node<K, V> 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<K, V> extends AbstractMap<K, V>
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to transition the node from the <tt>alive</tt> state to the
|
||||
* <tt>retired</tt> 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<K, V> extends AbstractMap<K, V>
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically transitions the node from the <tt>alive</tt> state to the
|
||||
* <tt>retired</tt> 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<K, V> extends AbstractMap<K, V>
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically transitions the node to the <tt>dead</tt> state and decrements
|
||||
* the <tt>weightedSize</tt>.
|
||||
* 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<K, V> extends AbstractMap<K, V>
|
||||
/**
|
||||
* 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<K, V> extends AbstractMap<K, V>
|
||||
}
|
||||
|
||||
/**
|
||||
* A value, its weight, and the entry's status.
|
||||
* A value, its selector, and the entry's status.
|
||||
*/
|
||||
static final class WeightedValue<V> {
|
||||
final int weight;
|
||||
@ -1178,7 +1178,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value held by the current <tt>WeightedValue</tt>.
|
||||
* Retrieves the value held by the current {@code WeightedValue}.
|
||||
*/
|
||||
V getValue() {
|
||||
return get().value;
|
||||
@ -1401,7 +1401,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<K, V> implements EntryWeigher<K, V>, Serializable {
|
||||
private static final long serialVersionUID = 1;
|
||||
@ -1520,7 +1520,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the initial capacity of the hash table (default <tt>16</tt>).
|
||||
* 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<K, V> extends AbstractMap<K, V>
|
||||
/**
|
||||
* Specifies the estimated number of concurrently updating threads. The
|
||||
* implementation performs internal sizing to try to accommodate this many
|
||||
* threads (default <tt>16</tt>).
|
||||
* threads (default {@code 16}).
|
||||
*
|
||||
* @param concurrencyLevel the estimated number of concurrently updating
|
||||
* threads
|
||||
@ -1584,9 +1584,9 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
/**
|
||||
* 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 <tt>1</tt>.
|
||||
* 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<K, V> extends AbstractMap<K, V>
|
||||
/**
|
||||
* 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 <tt>1</tt>.
|
||||
* 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
|
||||
*/
|
||||
|
@ -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 <K> 键类型
|
||||
@ -28,12 +28,12 @@ package org.dromara.hutool.core.map.concurrent;
|
||||
public interface EntryWeigher<K, V> {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
@ -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 <V> 值类型
|
||||
@ -27,11 +27,11 @@ package org.dromara.hutool.core.map.concurrent;
|
||||
public interface Weigher<V> {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
@ -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 <K> 键类型
|
||||
@ -54,7 +54,7 @@ public final class Weighers {
|
||||
}
|
||||
|
||||
/**
|
||||
* A weigher where an entry has a weight of <tt>1</tt>. A map bounded with
|
||||
* A weigher where an entry has a selector of <tt>1</tt>. 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 <tt>1</tt>. A map bounded with
|
||||
* A weigher where a value has a selector of <tt>1</tt>. 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.
|
||||
* <p>
|
||||
* A value with a weight of <tt>0</tt> 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 <tt>0</tt> 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.
|
||||
* <p>
|
||||
* A value with a weight of <tt>0</tt> 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 <tt>0</tt> 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 <E> 元素类型
|
||||
* @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.
|
||||
* <p>
|
||||
* A value with a weight of <tt>0</tt> 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 <tt>0</tt> 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 <E> 元素类型
|
||||
* @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.
|
||||
* <p>
|
||||
* A value with a weight of <tt>0</tt> 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 <tt>0</tt> 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 <E> 元素类型
|
||||
* @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.
|
||||
* <p>
|
||||
* A value with a weight of <tt>0</tt> 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 <tt>0</tt> 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 <E> 元素类型
|
||||
* @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.
|
||||
* <p>
|
||||
* A value with a weight of <tt>0</tt> 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 <tt>0</tt> 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 <K> 键类型
|
||||
* @param <V> 值类型
|
||||
|
@ -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开始计算 默认此配置为空,即不限制
|
||||
|
@ -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 <T> 随机对象类型
|
||||
* @param weightObjs 带有权重的对象列表
|
||||
* @return {@link WeightRandom}
|
||||
* @return {@link WeightRandomSelector}
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public static <T> WeightRandom<T> weightRandom(final WeightObj<T>[] weightObjs) {
|
||||
return new WeightRandom<>(weightObjs);
|
||||
public static <T> WeightRandomSelector<T> weightRandom(final WeightObj<T>[] weightObjs) {
|
||||
return new WeightRandomSelector<>(weightObjs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -649,11 +643,11 @@ public class RandomUtil {
|
||||
*
|
||||
* @param <T> 随机对象类型
|
||||
* @param weightObjs 带有权重的对象列表
|
||||
* @return {@link WeightRandom}
|
||||
* @return {@link WeightRandomSelector}
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public static <T> WeightRandom<T> weightRandom(final Iterable<WeightObj<T>> weightObjs) {
|
||||
return new WeightRandom<>(weightObjs);
|
||||
public static <T> WeightRandomSelector<T> weightRandom(final Iterable<WeightObj<T>> weightObjs) {
|
||||
return new WeightRandomSelector<>(weightObjs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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<String> 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));
|
||||
}
|
||||
}
|
@ -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<String> 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));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user