如何在 java 中随机化 Hashmap 中的相同值

How to randomize the same values in Hashmap in java

 Map<Integer, BigDecimal> vendorRating = new HashMap<>();
List<Integer> sortedList = new LinkedList<>();

vendorRating 有值 (11=>4,12=>3.5,13=>3,14=>3.5,15=>4,16=>5)

我想根据值对键进行排序,并将相同的值打乱。

输出应该像 [16,11,15,12,14,13], [16,15,11,14,12,13],[16,15,11,12,14,13] , [16,11,15,14,12,13]

我试过如下,这对排序有效,但我不知道我要洗牌相同的值。

  vendorRating.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
            .forEachOrdered(
                x -> sortedList.addAll(Collections.singleton(x.getKey()))
        );

假设您的地图如下:

Map<Integer, BigDecimal> vendorRating = Map.of( 11, BigDecimal.valueOf(4),
                                                12, BigDecimal.valueOf(3.5),
                                                13, BigDecimal.valueOf(3),
                                                14, BigDecimal.valueOf(3.5),
                                                15, BigDecimal.valueOf(4),
                                                16, BigDecimal.valueOf(5));

您首先需要一个比较器来按需要按降序排序:

Comparator<Map.Entry<Integer, BigDecimal>> comp = Map.Entry.comparingByValue();

在流中使用上述比较器,然后您可以使用 Collectors.groupingBy 对具有相同值的条目进行分组以生成 Map<BigDecimal,List<Integer>>

Map<BigDecimal,List<Integer>> grouped =
            
    vendorRating.entrySet()
            .stream()
            .sorted(comp.reversed())
            .collect(Collectors.groupingBy(Map.Entry::getValue, LinkedHashMap::new,
                    Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
    
    System.out.println(grouped);

//output: {5=[16], 4=[11, 15], 3.5=[12, 14], 3=[13]}

由于您似乎只对原始地图的键感兴趣,而不是 BigDecimal 值,因此我将在下面仅使用上述地图 grouped.values() 的值,即 [[16], [11, 15], [12, 14], [13]]

有趣的部分来了。现在你需要生成每个列表的所有排列,这不是一项微不足道的任务,但可以自己编写一个算法来做到这一点。但是像 Guava、Apache commons 这样的库已经有这样的功能了。我建议使用库而不是实现排列生成算法。我将使用 combinatoricslib3,我发现它在处理流时非常方便。

Maven 依赖:

<dependency>
   <groupId>com.github.dpaukov</groupId>
   <artifactId>combinatoricslib3</artifactId>
   <version>3.3.2</version>
</dependency>

正如您在上面链接页面中使用 combinatoricslib3 看到的示例,您可以非常轻松地生成排列,语法也很简单,生成一些字符串排列的示例

Generator.permutation("apple", "orange", "cherry")
   .simple()
   .stream()
   .forEach(System.out::println);

//会输出

[apple, orange, cherry]
[apple, cherry, orange]
[cherry, apple, orange]
[cherry, orange, apple]
[orange, cherry, apple]
[orange, apple, cherry]

应用于您的用例,生成每个子列表的排列可能看起来像

grouped.values().stream()
            .map(list -> Generator.permutation(list).simple().stream().collect(Collectors.toList()))
            .collect(Collectors.toList()).forEach(System.out::println);

会给你:

[[16]]
[[15, 11], [11, 15]]
[[12, 14], [14, 12]]
[[13]]

接近您想要的最终结果,但还差得远。您现在需要一次从每个子列表中选择一个元素以获得最终结果,这与生成列表的笛卡尔积相同。幸运的是,combinatoricslib3 也可以做到这一点。但我确信像 Guava 这样的其他库也有相同的实现。语法就像上面一样非常简单。如果您需要更多信息,请查看他们的示例。

首先将中间结果存储在列表列表中:

List<List<List<Integer>>> perms = grouped.values().stream()
                                            .map(list -> Generator.permutation(list)
                                                    .simple().stream()
                                                    .collect(Collectors.toList()))
                                            .collect(Collectors.toList());

//and generating the cartasian product

Generator.cartesianProduct(perms)
            .stream()
            .forEach(System.out::println);

//should produce something like:

[[16], [15, 11], [14, 12], [13]]
[[16], [15, 11], [12, 14], [13]]
[[16], [11, 15], [14, 12], [13]]
[[16], [11, 15], [12, 14], [13]]

这几乎就是您的最终结果。您只需流式传输内部列表和平面图即可将它们收集到一个列表中:

Generator.cartesianProduct(perms)
            .stream()
            .map(lili -> lili.stream().flatMap(List::stream).collect(Collectors.toList()))
            .collect(Collectors.toList())
            .forEach(System.out::println);

最终将带您到达目的地:

[16, 15, 11, 14, 12, 13]
[16, 15, 11, 12, 14, 13]
[16, 11, 15, 14, 12, 13]
[16, 11, 15, 12, 14, 13]

我将整个任务分解成更小的步骤,以便于解释。如果您对存储中间结果不感兴趣,而只对最终输出感兴趣,您可以内联这些步骤并一次性完成。工作演示:

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.paukov.combinatorics3.Generator;

public class Example {

    public static void main(String[] args) {
        Map<Integer, BigDecimal> vendorRating = Map.of( 11, BigDecimal.valueOf(4),
                                                        12, BigDecimal.valueOf(3.5),
                                                        13, BigDecimal.valueOf(3),
                                                        14, BigDecimal.valueOf(3.5),
                                                        15, BigDecimal.valueOf(4),
                                                        16, BigDecimal.valueOf(5));

        Comparator<Map.Entry<Integer, BigDecimal>> comp = Map.Entry.comparingByValue();
        
        List<List<Integer>> result =
        Generator.cartesianProduct(
                vendorRating.entrySet()
                        .stream()
                        .sorted(comp.reversed())
                        .collect(Collectors.groupingBy(Map.Entry::getValue, LinkedHashMap::new,
                                Collectors.mapping(Map.Entry::getKey, Collectors.toList())))
                        .values().stream()
                        .map(list -> Generator.permutation(list).simple().stream().collect(Collectors.toList()))
                        .collect(Collectors.toList())
        ).stream()
                .map(lili -> lili.stream().flatMap(List::stream).collect(Collectors.toList()))
                .collect(Collectors.toList());

        result.forEach(System.out::println);        
    }
}

输出

[16, 15, 11, 14, 12, 13]
[16, 15, 11, 12, 14, 13]
[16, 11, 15, 14, 12, 13]
[16, 11, 15, 12, 14, 13]

答案变得比原先想象的要长得多。这是长的土豆post

System.out.println("Potato");