mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +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
|
* concurrency for updates, and a maximum capacity to bound the map by. This
|
||||||
* implementation differs from {@link ConcurrentHashMap} in that it maintains a
|
* implementation differs from {@link ConcurrentHashMap} in that it maintains a
|
||||||
* page replacement algorithm that is used to evict an entry when the map has
|
* 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
|
* map does not have a publicly visible constructor and instances are created
|
||||||
* through a {@link Builder}.
|
* through a {@link Builder}.
|
||||||
* <p>
|
* <p>
|
||||||
* An entry is evicted from the map when the <tt>weighted capacity</tt> exceeds
|
* An entry is evicted from the map when the {@code weighted capacity} exceeds
|
||||||
* its <tt>maximum weighted capacity</tt> threshold. A {@link EntryWeigher}
|
* its {@code maximum weighted capacity} threshold. A {@link EntryWeigher}
|
||||||
* determines how many units of capacity that an entry consumes. The default
|
* 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
|
* 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
|
* 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
|
* 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.
|
* map.
|
||||||
* <p>
|
* <p>
|
||||||
* An {@link BiConsumer} may be supplied for notification when an entry
|
* 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
|
* operation asynchronously, such as by submitting a task to an
|
||||||
* {@link java.util.concurrent.ExecutorService}.
|
* {@link java.util.concurrent.ExecutorService}.
|
||||||
* <p>
|
* <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
|
* concurrently modify the table. Using a significantly higher or lower value
|
||||||
* than needed can waste space or lead to thread contention, but an estimate
|
* 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
|
* within an order of magnitude of the ideal value does not usually have a
|
||||||
@ -75,7 +75,7 @@ import java.util.function.BiConsumer;
|
|||||||
* interfaces.
|
* interfaces.
|
||||||
* <p>
|
* <p>
|
||||||
* Like {@link Hashtable} but unlike {@link HashMap}, this class
|
* 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
|
* {@link LinkedHashMap}, this class does <em>not</em> provide
|
||||||
* predictable iteration order. A snapshot of the keys and entries may be
|
* predictable iteration order. A snapshot of the keys and entries may be
|
||||||
* obtained in ascending and descending order of retention.
|
* 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
|
* 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
|
* 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.
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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();
|
final Node<K, V> node = evictionDeque.poll();
|
||||||
|
|
||||||
// If weighted values are used, then the pending operations will adjust
|
// 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) {
|
if (node == null) {
|
||||||
return;
|
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
|
* Attempts to transition the node from the {@code alive} state to the
|
||||||
* <tt>retired</tt> state.
|
* {@code retired} state.
|
||||||
*
|
*
|
||||||
* @param node the entry in the page replacement policy
|
* @param node the entry in the page replacement policy
|
||||||
* @param expect the expected weighted value
|
* @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
|
* Atomically transitions the node from the {@code alive} state to the
|
||||||
* <tt>retired</tt> state, if a valid transition.
|
* {@code retired} state, if a valid transition.
|
||||||
*
|
*
|
||||||
* @param node the entry in the page replacement policy
|
* @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
|
* Atomically transitions the node to the {@code dead} state and decrements
|
||||||
* the <tt>weightedSize</tt>.
|
* the {@code weightedSize}.
|
||||||
*
|
*
|
||||||
* @param node the entry in the page replacement policy
|
* @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.
|
* 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() {
|
public long weightedSize() {
|
||||||
return Math.max(0, weightedSize.get());
|
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> {
|
static final class WeightedValue<V> {
|
||||||
final int weight;
|
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() {
|
V getValue() {
|
||||||
return get().value;
|
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 {
|
static final class BoundedEntryWeigher<K, V> implements EntryWeigher<K, V>, Serializable {
|
||||||
private static final long serialVersionUID = 1;
|
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
|
* This is the number of key-value pairs that the hash table can hold
|
||||||
* before a resize operation is required.
|
* 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
|
* Specifies the estimated number of concurrently updating threads. The
|
||||||
* implementation performs internal sizing to try to accommodate this many
|
* 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
|
* @param concurrencyLevel the estimated number of concurrently updating
|
||||||
* threads
|
* 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
|
* Specifies an algorithm to determine how many the units of capacity a
|
||||||
* value consumes. The default algorithm bounds the map by the number of
|
* 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
|
* @return this
|
||||||
* @throws NullPointerException if the weigher is null
|
* @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
|
* Specifies an algorithm to determine how many the units of capacity an
|
||||||
* entry consumes. The default algorithm bounds the map by the number of
|
* 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
|
* @return this
|
||||||
* @throws NullPointerException if the weigher is null
|
* @throws NullPointerException if the weigher is null
|
||||||
*/
|
*/
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
package org.dromara.hutool.core.map.concurrent;
|
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.
|
* is used to determine when an eviction is required.
|
||||||
*
|
*
|
||||||
* @param <K> 键类型
|
* @param <K> 键类型
|
||||||
@ -28,12 +28,12 @@ package org.dromara.hutool.core.map.concurrent;
|
|||||||
public interface EntryWeigher<K, V> {
|
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.
|
* the key and value consumes. An entry must consume a minimum of one unit.
|
||||||
*
|
*
|
||||||
* @param key the key to weigh
|
* @param key the key to weigh
|
||||||
* @param value the value to weigh
|
* @param value the value to weigh
|
||||||
* @return the entry's weight
|
* @return the entry's selector
|
||||||
*/
|
*/
|
||||||
int weightOf(K key, V value);
|
int weightOf(K key, V value);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
package org.dromara.hutool.core.map.concurrent;
|
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.
|
* is used to determine when an eviction is required.
|
||||||
*
|
*
|
||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
@ -27,11 +27,11 @@ package org.dromara.hutool.core.map.concurrent;
|
|||||||
public interface Weigher<V> {
|
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.
|
* the value consumes. A value must consume a minimum of one unit.
|
||||||
*
|
*
|
||||||
* @param value the object to weigh
|
* @param value the object to weigh
|
||||||
* @return the object's weight
|
* @return the object's selector
|
||||||
*/
|
*/
|
||||||
int weightOf(V value);
|
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
|
* A entry weigher backed by the specified weigher. The selector of the value
|
||||||
* determines the weight of the entry.
|
* determines the selector of the entry.
|
||||||
*
|
*
|
||||||
* @param weigher the weigher to be "wrapped" in a entry weigher.
|
* @param weigher the weigher to be "wrapped" in a entry weigher.
|
||||||
* @param <K> 键类型
|
* @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
|
* this weigher will evict when the number of key-value pairs exceeds the
|
||||||
* capacity.
|
* 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
|
* this weigher will evict when the number of key-value pairs exceeds the
|
||||||
* capacity.
|
* 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
|
* 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.
|
* 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
|
* This allows for restricting the capacity based on the memory-consumption
|
||||||
* and is primarily for usage by dedicated caching servers that hold the
|
* and is primarily for usage by dedicated caching servers that hold the
|
||||||
* serialized data.
|
* serialized data.
|
||||||
* <p>
|
* <p>
|
||||||
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
* A value with a selector 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
|
* with this selector can occur then the caller should eagerly evaluate the
|
||||||
* value and treat it as a removal operation. Alternatively, a custom weigher
|
* 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.
|
* @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
|
* number of elements. This weigher only should be used when the alternative
|
||||||
* {@link #collection()} weigher cannot be, as evaluation takes O(n) time. A
|
* {@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
|
* 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.
|
* exceeds the capacity rather than the number of key-value pairs in the map.
|
||||||
* <p>
|
* <p>
|
||||||
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
* A value with a selector 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
|
* with this selector can occur then the caller should eagerly evaluate the
|
||||||
* value and treat it as a removal operation. Alternatively, a custom weigher
|
* 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> 元素类型
|
* @param <E> 元素类型
|
||||||
* @return A weigher where each element takes one unit of capacity.
|
* @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
|
* 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
|
* total number of elements exceeds the capacity rather than the number of
|
||||||
* key-value pairs in the map.
|
* key-value pairs in the map.
|
||||||
* <p>
|
* <p>
|
||||||
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
* A value with a selector 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
|
* with this selector can occur then the caller should eagerly evaluate the
|
||||||
* value and treat it as a removal operation. Alternatively, a custom weigher
|
* 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> 元素类型
|
* @param <E> 元素类型
|
||||||
* @return A weigher where each element takes one unit of capacity.
|
* @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
|
* of elements. A map bounded with this weigher will evict when the total
|
||||||
* number of elements exceeds the capacity rather than the number of
|
* number of elements exceeds the capacity rather than the number of
|
||||||
* key-value pairs in the map.
|
* key-value pairs in the map.
|
||||||
* <p>
|
* <p>
|
||||||
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
* A value with a selector 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
|
* with this selector can occur then the caller should eagerly evaluate the
|
||||||
* value and treat it as a removal operation. Alternatively, a custom weigher
|
* 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> 元素类型
|
* @param <E> 元素类型
|
||||||
* @return A weigher where each element takes one unit of capacity.
|
* @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
|
* of elements. A map bounded with this weigher will evict when the total
|
||||||
* number of elements exceeds the capacity rather than the number of
|
* number of elements exceeds the capacity rather than the number of
|
||||||
* key-value pairs in the map.
|
* key-value pairs in the map.
|
||||||
* <p>
|
* <p>
|
||||||
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
* A value with a selector 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
|
* with this selector can occur then the caller should eagerly evaluate the
|
||||||
* value and treat it as a removal operation. Alternatively, a custom weigher
|
* 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> 元素类型
|
* @param <E> 元素类型
|
||||||
* @return A weigher where each element takes one unit of capacity.
|
* @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. 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
|
* entries across all values exceeds the capacity rather than the number of
|
||||||
* key-value pairs in the map.
|
* key-value pairs in the map.
|
||||||
* <p>
|
* <p>
|
||||||
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
* A value with a selector 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
|
* with this selector can occur then the caller should eagerly evaluate the
|
||||||
* value and treat it as a removal operation. Alternatively, a custom weigher
|
* 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 <K> 键类型
|
||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
|
@ -30,7 +30,7 @@ public class TreeNodeConfig implements Serializable {
|
|||||||
// 属性名配置字段
|
// 属性名配置字段
|
||||||
private String idKey = "id";
|
private String idKey = "id";
|
||||||
private String parentIdKey = "parentId";
|
private String parentIdKey = "parentId";
|
||||||
private String weightKey = "weight";
|
private String weightKey = "selector";
|
||||||
private String nameKey = "name";
|
private String nameKey = "name";
|
||||||
private String childrenKey = "children";
|
private String childrenKey = "children";
|
||||||
// 可以配置递归深度 从0开始计算 默认此配置为空,即不限制
|
// 可以配置递归深度 从0开始计算 默认此配置为空,即不限制
|
||||||
|
@ -20,8 +20,8 @@ import org.dromara.hutool.core.date.DateTime;
|
|||||||
import org.dromara.hutool.core.date.DateUtil;
|
import org.dromara.hutool.core.date.DateUtil;
|
||||||
import org.dromara.hutool.core.exception.HutoolException;
|
import org.dromara.hutool.core.exception.HutoolException;
|
||||||
import org.dromara.hutool.core.lang.Assert;
|
import org.dromara.hutool.core.lang.Assert;
|
||||||
import org.dromara.hutool.core.lang.WeightRandom;
|
import org.dromara.hutool.core.lang.selector.WeightObj;
|
||||||
import org.dromara.hutool.core.lang.WeightRandom.WeightObj;
|
import org.dromara.hutool.core.lang.selector.WeightRandomSelector;
|
||||||
import org.dromara.hutool.core.math.NumberUtil;
|
import org.dromara.hutool.core.math.NumberUtil;
|
||||||
import org.dromara.hutool.core.text.StrUtil;
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
|
|
||||||
@ -29,13 +29,7 @@ import java.math.BigDecimal;
|
|||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
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.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -637,11 +631,11 @@ public class RandomUtil {
|
|||||||
*
|
*
|
||||||
* @param <T> 随机对象类型
|
* @param <T> 随机对象类型
|
||||||
* @param weightObjs 带有权重的对象列表
|
* @param weightObjs 带有权重的对象列表
|
||||||
* @return {@link WeightRandom}
|
* @return {@link WeightRandomSelector}
|
||||||
* @since 4.0.3
|
* @since 4.0.3
|
||||||
*/
|
*/
|
||||||
public static <T> WeightRandom<T> weightRandom(final WeightObj<T>[] weightObjs) {
|
public static <T> WeightRandomSelector<T> weightRandom(final WeightObj<T>[] weightObjs) {
|
||||||
return new WeightRandom<>(weightObjs);
|
return new WeightRandomSelector<>(weightObjs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -649,11 +643,11 @@ public class RandomUtil {
|
|||||||
*
|
*
|
||||||
* @param <T> 随机对象类型
|
* @param <T> 随机对象类型
|
||||||
* @param weightObjs 带有权重的对象列表
|
* @param weightObjs 带有权重的对象列表
|
||||||
* @return {@link WeightRandom}
|
* @return {@link WeightRandomSelector}
|
||||||
* @since 4.0.3
|
* @since 4.0.3
|
||||||
*/
|
*/
|
||||||
public static <T> WeightRandom<T> weightRandom(final Iterable<WeightObj<T>> weightObjs) {
|
public static <T> WeightRandomSelector<T> weightRandom(final Iterable<WeightObj<T>> weightObjs) {
|
||||||
return new WeightRandom<>(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