根据设置的概率,取随机到的数据元素

1、包结构

java根据指定概率指定库存数量进行抽奖_List

 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;
		
	}

}