Gradle
implementation group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1'
Maven
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-math3</artifactId>
    <version>3.6.1</version>
</dependency>
Test
public class GachaUnitTest {
  @BeforeAll
  public static void setup() {
  }
  @Test
  @DisplayName("가챠 확률 테스트")
  public void should_GetRandomItem_When_Gacha() {
    List<Pair<String, Double>> items = getItems();
    List<String> itemsName = Lists.newArrayList();
    for (int testCount = 0; testCount < 1000; testCount++) {
      String itemName = getGachaItemName(items);
      boolean containsItem = items
          .stream()
          .anyMatch(stringDoublePair -> stringDoublePair.getKey().equals(itemName));
      assertThat(containsItem, is(true));
      itemsName.add(itemName);
    }
    List<Map<String, List<String>>> sortedItemMap = itemsName
        .stream()
        .collect(Collectors.groupingBy(i -> i))
        .entrySet()
        .stream()
        .sorted(Comparator.comparing(i -> i.getValue().size(), Comparator.reverseOrder()))
        .map(entry -> {
          Map<String, List<String>> map = Maps.newLinkedHashMap();
          map.put(entry.getKey(), entry.getValue());
          return map;
        })
        .toList();
    for (Map<String, List<String>> itemMap : sortedItemMap) {
      for (Map.Entry<String, List<String>> item : itemMap.entrySet()) {
        String key = item.getKey();
        List<String> values = item.getValue();
        int size = values.size();
        System.out.println(key + " - " + size);
      }
    }
  }
  private String getGachaItemName(List<Pair<String, Double>> items) {
    return new EnumeratedDistribution<>(items).sample();
  }
  private List<Pair<String, Double>> getItems() {
    Pair<String, Double> item1 = new Pair<>("item1", 1.5);
    Pair<String, Double> item2 = new Pair<>("item2", 4.1);
    Pair<String, Double> item3 = new Pair<>("item3", 3.7);
    Pair<String, Double> item4 = new Pair<>("item4", 7.3);
    Pair<String, Double> item5 = new Pair<>("item5", 2.0);
    Pair<String, Double> item6 = new Pair<>("item6", 2.8);
    Pair<String, Double> item7 = new Pair<>("item7", 6.9);
    Pair<String, Double> item8 = new Pair<>("item8", 9.0);
    Pair<String, Double> item9 = new Pair<>("item9", 8.8);
    Pair<String, Double> item10 = new Pair<>("item10", 5.2);
    return Lists.newArrayList(item1, item2, item3, item4, item5, item6, item7, item8, item9,
        item10);
  }
}
결과
item8 - 184
item9 - 155
item7 - 147
item4 - 124
item10 - 103
item2 - 84
item3 - 64
item6 - 62
item5 - 52
item1 - 25
확률이기 때문에 간헐적으로 item9가 더 많이 나올때도 있지만 item8과 그리 많이 차이나지 않는다.
사용법
- 라이브러리 추가
 - List<Pair<T, 가중치 Double>> 형태로 된 데이터 준비 (보통은 T를 Entity로 많이 사용할 듯)
 - new EnumeratedDistribution<>(items).sample();으로 랜덤 아이템 획득