根据设置的概率,取随机到的数据元素
1、包结构
2、测试用例代码
package com.coolsn.modules.tb.probability.test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.coolsn.modules.tb.probability.ProbabilityReward;
import com.google.common.collect.Lists;
import net.sf.json.JSONArray;
public class TestMain {
public static void main(String[] args) {
testMapData();
}
/**
* 测试注解形式的
*/
public static void testBeanAnn() {
List<Object> testBeans = Lists.newArrayList();
testBeans.add(new TestBean("1", 0.1, "新电饭锅"));
testBeans.add(new TestBean("2", 0.15, "有点旧的电饭锅"));
testBeans.add(new TestBean("3", 0.2, "显示灯坏了的电饭锅"));
testBeans.add(new TestBean("4", 0.25, "蒸不熟饭的电饭锅"));
ProbabilityReward probabilityReward = new ProbabilityReward();
Object item = probabilityReward.getProbabilityItem(testBeans);
if (item == null) {
System.out.println("凡人啊,恭喜你,中了个寂寞");
}else {
TestBean testBean = (TestBean)item;
System.out.println("哇,你中了个“"+testBean.getGoodsName()+"”");
}
}
/**
* 测试List-Map的数据形式
*/
public static void testMapData() {
List<TestBean> testBeans = Lists.newArrayList();
testBeans.add(new TestBean("1", 0.1, "新电饭锅"));
testBeans.add(new TestBean("2", 0.15, "有点旧的电饭锅"));
testBeans.add(new TestBean("3", 0.2, "显示灯坏了的电饭锅"));
testBeans.add(new TestBean("4", 0.25, "蒸不熟饭的电饭锅"));
//这里懒得组装map数据了,直接用之前的bean转吧QAQ
JSONArray jsonArray = JSONArray.fromObject(testBeans);
@SuppressWarnings("unchecked")
List<Map<String, Object>> testMaps = (List<Map<String, Object>>) JSONArray.toCollection(jsonArray,HashMap.class);
ProbabilityReward probabilityReward = new ProbabilityReward();
Object item = probabilityReward.getProbabilityItem(testMaps,"goodsProbability");
if (item == null) {
System.out.println("凡人啊,恭喜你,中了个寂寞");
}else {
Map<String, Object> map = (HashMap)item;
System.out.println("哇,你中了个“"+map.get("goodsName")+"”");
}
}
}
3、自定义注解代码
package com.coolsn.modules.tb.probability.entity;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 概率注解
*
* 注解的值需要是double的
*
* @author tangbin
* @date 2021年9月26日
*/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ProbabilityUtilsAnnotation {
/**
* 是否是概率字段
* @return
*/
boolean probability() default false;
}
4、测试用例实体bean
package com.coolsn.modules.tb.probability.test;
import com.coolsn.modules.tb.probability.entity.ProbabilityUtilsAnnotation;
public class TestBean {
//奖励的商品id
private String goodsId;
//该商品的概率
@ProbabilityUtilsAnnotation(probability = true)
private Double goodsProbability;
//奖励的商品名称
private String goodsName;
public TestBean() {}
public TestBean(String goodsId,Double goodsProbability,String goodsName) {
this.goodsId = goodsId;
this.goodsProbability = goodsProbability;
this.goodsName = goodsName;
}
public String getGoodsId() {
return goodsId;
}
public void setGoodsId(String goodsId) {
this.goodsId = goodsId;
}
public Double getGoodsProbability() {
return goodsProbability;
}
public void setGoodsProbability(Double goodsProbability) {
this.goodsProbability = goodsProbability;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
}
5、概率处理工具类
package com.coolsn.modules.tb.probability;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import com.coolsn.modules.tb.probability.entity.ProbabilityUtilsAnnotation;
import com.google.common.collect.Lists;
/**
* @time 2021-09-26
* @author tb
* @Description 处理普通的按概率取数据
*/
public class ProbabilityReward {
/**
* 概率列表
*/
private List<Double> probabilityList;
/**
*
* 获取随机到的item
*
* @param itemsList item列表
* @return
*/
public Object getProbabilityItem(List<Object> itemsList) {
//没查到数据
if (itemsList == null || itemsList.isEmpty()) {
return null;
}
//取概率列表
this.probabilityList = getProbabilityList(itemsList);
if (probabilityList == null || probabilityList.size() == 0) {
//未取到概率列表数据
return null;
}
//取随机到的索引
double pointValue=Math.random();//取0~1之间的随机数
int index = getIndex(pointValue,0,0);
if (index >= probabilityList.size()) {
//未随机到合适的索引
return null;
}
return itemsList.get(index);
}
/**
*
* 获取随机到的item
*
* @param itemsList 数据元素列表
* @param probabilityKey 概率元素的key
* @return
*/
public Map<String, Object> getProbabilityItem(List<Map<String, Object>> itemsList,String probabilityKey) {
//没查到数据
if (itemsList == null || itemsList.isEmpty()) {
return null;
}
//取概率列表
this.probabilityList = getProbabilityList(itemsList,probabilityKey);
if (probabilityList == null || probabilityList.size() == 0) {
//未取到概率列表数据
return null;
}
//取随机到的索引
double pointValue=Math.random();//取0~1之间的随机数
int index = getIndex(pointValue,0,0);
if (index >= probabilityList.size()) {
//未随机到合适的索引
return null;
}
return itemsList.get(index);
}
/**
* 通过递归的方式找到最符合的概率数组下标,为了处理概率数组长度变化的情况
* @param random 0~1的随机数,对比值
* @param index 概率数组的下标,一般初始为0
* @param startValue 0
* @return index 最适合的概率下标
*
* 概率区间原理说明:例如有pro[0.1,0.2,0.3,0.3,0.1] 那么从0~1之间随机取的值在0~0.1,0.1~0.3,0.3~0.6,0.6~0.9,0.9~1之间,pro越大,区间度越大,取到的概率也就越大。
*/
private int getIndex(double pointValue,int index,double startValue){
if (index >= probabilityList.size()) {
//表示所有元素都没有在随机区间,原因例如,概率列表为[0.1,0,0.4,0],随机的标识值为0.8,即概率列表总和不为1的时候
//直接返回该索引
return index;
}
if(startValue<=pointValue&&pointValue<(probabilityList.get(index)+startValue)){//比较随机到的数区间,如果不符合则换下一个区间
return index;
}
return getIndex(pointValue,index+1,probabilityList.get(index)+startValue);
}
/**
* 取概率列表
*/
private List<Double> getProbabilityList(List<Map<String, Object>> itemsList,String probabilityKey) {
//判断参数
if (itemsList == null || itemsList.size() == 0) {
return null;
}
//概率列表
List<Double> probabilityList = Lists.newArrayList();
//取概率列表
for (int i = 0; i < itemsList.size(); i++) {
Double probability = 0d;
Map<String, Object> itemMap = itemsList.get(i);
if (itemMap != null) {
if (itemMap.containsKey(probabilityKey)) {
Object probabilityObject = itemMap.get(probabilityKey);
if (probabilityObject != null && probabilityObject instanceof Double) {
probability = (Double)probabilityObject;
}
}
}
//加入概率列表
probabilityList.add(probability);
}
return probabilityList;
}
/**
* 取概率列表
*/
private List<Double> getProbabilityList(List<Object> itemsList) {
//判断参数
if (itemsList == null || itemsList.size() == 0) {
return null;
}
//概率列表
List<Double> probabilityList = Lists.newArrayList();
//通过反射取概率列表
for (int i = 0; i < itemsList.size(); i++) {
Object itemObject = itemsList.get(i);
Class<?> c = itemObject.getClass();
//该item的概率
Double probability = 0d;
//通过反射取属性的值
Field[] fields = c.getDeclaredFields();
for (int j = 0; j < fields.length; j++) {
Field field = fields[j];
field.setAccessible(true);
//取该属性的概率注解
ProbabilityUtilsAnnotation[] probabilityUtilsAnnotations = field.getDeclaredAnnotationsByType(ProbabilityUtilsAnnotation.class);
//取注解信息
for (int k = 0; probabilityUtilsAnnotations != null && k < probabilityUtilsAnnotations.length; k++) {
ProbabilityUtilsAnnotation probabilityUtilsAnnotation = probabilityUtilsAnnotations[k];
boolean probabilityAnn = probabilityUtilsAnnotation.probability();
if (probabilityAnn) {
//表示是概率字段,取该概率字段的值,只会匹配到一个概率注解
try {
Object value = field.get(itemObject);
if (value != null && value instanceof Double) {
//如果值不为空,则表示有概率值
probability = (double)value;
}
} catch (IllegalArgumentException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
if (probability > 0) {
//表示取到了概率标注属性,跳出
break;
}
}
//加入概率列表
probabilityList.add(probability);
}
return probabilityList;
}
}