查找与另一个键至少包含n个元素的键,包含列表

时间:2018-07-21 15:53:33

标签: java java-8 linkedhashmap

我有LinkedHashMap的{​​{1}}。我正在构建地图,因此也许有更好的方法来组织所有数据。

我正在尝试获取具有公共列表的键,每个列表中至少有2个相同的元素。

例如:

<String, List<T>>

最后,我希望有以下列表:Map ---------------------- | Key | Values | ---------------------- | M1 | [A1, A3] | | M2 | [A1, A2, A3] | | M3 | [A1, A2] | | M4 | [A2, A3] | ----------------------

  • M2和M3同时包含A1和A2
  • M2和M4同时包含A2和A3
  • M1和M2都包含A1和A3

我一直在努力弄清楚如何将我的第一个条目的值与所有其他条目的值进行比较。依此类推,直到到达地图的末尾(如列表的双[ [M2, M3], [M2, M4], [M1, M2] ]循环)。

我现在的解决方案(但我绝对觉得有更好的方法)

for

正在使用地图吗?

性能暂时不是问题。但是,获得平滑的效果总是更好的选择

5 个答案:

答案 0 :(得分:0)

您可以使用以下方法

1)迭代映射中每个键的值(将是一个列表)

2)从上述迭代的下一个索引开始另一个迭代,直到结束

3)对于#1列表中的每个元素,请使用contains方法检查其是否在#2列表中

a) Finish iterating list #3 as soon as two identical objects are found 

b) Iterate list in #3 till last but one if no element one

c) Iterate list in #3 till last if one element found

希望对您有帮助

答案 1 :(得分:0)

您在这里。我用String代替Generic:

static Set<String> getCommonKeySet() {
        Map<String, List<String>> map = new HashMap<>();
        Set<String> listOfKeysWithCommonValueSet = new HashSet<>();

        map.forEach((key, value) -> {
            map.entrySet().forEach(entry1 -> {
                List<String> list = new ArrayList<>(value);
                List<String> list1 = new ArrayList<>(entry1.getValue());
                list.retainAll(list1);
                if (list.size() >= 2)
                    listOfKeysWithCommonValueSet.add(key);
            });
        });
        return listOfKeysWithCommonValueSet;
    }

编辑:

要返回Set<Set<String>>,请使用以下代码:

static Set<Set<String>> getCommonKeySet() {

    Map<String, List<String>> map = new LinkedHashMap<>();
    map.put("M1", Arrays.asList("A1", "A3"));
    map.put("M2", Arrays.asList("A1", "A2", "A3"));
    map.put("M3", Arrays.asList("A1", "A2"));
    map.put("M4", Arrays.asList("A2", "A3"));
    //Map<String, List<String>> map = new HashMap<>();
    Set<Set<String>> listOfKeysWithCommonValueSet = new HashSet<>();

    map.forEach((key, value) -> {
        map.entrySet().forEach(entry1 -> {
            if(!entry1.getKey().equals(key))
            {
                List<String> list = new ArrayList<>(value);
                List<String> list1 = new ArrayList<>(entry1.getValue());
                list.retainAll(list1);
                if (list.size() >= 2)
                {
                    Set<String> set = new HashSet<>();
                    set.add(key);
                    set.add(entry1.getKey());
                    listOfKeysWithCommonValueSet.add(set);
                }
            }
        });
    });

    System.out.println(listOfKeysWithCommonValueSet);
    return listOfKeysWithCommonValueSet;
}

输出:

  

[[M1,M2],[M2,M3],[M2,M4]]

答案 2 :(得分:0)

正如我所注意到的,您需要List<List<Output>>,它对应于结构[ [M2, M3], [M2, M4], [M1, M2] ]

考虑非常相同的输入:

Map<String, List<String>> map = new LinkedHashMap<>();      
map.put("M1", Arrays.asList("A1", "A3"));
map.put("M2", Arrays.asList("A1", "A2", "A3"));
map.put("M3", Arrays.asList("A1", "A2"));
map.put("M4", Arrays.asList("A2", "A3"));

这是可行的解决方案:

List<List<String>> output = new ArrayList<>();   // The output List
Set<String> keys = new HashSet<>();              // Key storage used to avoid comparison                            
                                                 // of the keys twice (M1-M2, M2-M1)

for (Entry<String, List<String>> entryOuter: map.entrySet()) {               // First iteration
    if (keys.add(entryOuter.getKey())) {                                     // Adds a new key
        for (Entry<String, List<String>> entryInner: map.entrySet()) {       // Second iteration 
            if (!keys.contains(entryInner.getKey())) {                       // To compare?
                List<String> common = new ArrayList<>(entryOuter.getValue());
                common.retainAll(new ArrayList<>(entryInner.getValue()));    // The common items
                if (common.size() > 1) {                                     // At least 2 common?
                    output.add(Arrays.asList{
                        entryOuter.getKey(), entryInner.getKey()));          // Add these keys
                }
            }
        }
    }       
}

调用System.out.println(output);将显示所需的结果:

  

[[M1,M2],[M2,M3],[M2,M4]]

简要地描述了这个想法:

  • 目标是将每个密钥仅用一个不同的密钥进行一次迭代-实现6次迭代。
  • 使用Set<String> keys存储“已选中”键。
  • 发生唯一组合时,找到共同的值。
  • 如果公用值的个数为2个或更多,则将键成对地添加到输出List中。
  • Voilà,任务完成了。

您已经标记了,所以我建议您可能要使用,这在这里没有真正的好处。除非您实施变通方法,否则不会帮助您轻松地使用索引进行迭代:How get value from LinkedHashMap based on index not on key?

答案 3 :(得分:0)

基本思路是执行以下步骤:

1)原始输入图。

+------+--------------+
| Keys | Values       |
+------+--------------+
| M1   | [A1, A3]     |
+------+--------------+
| M2   | [A1, A2, A3] |
+------+--------------+
| M3   | [A1, A2]     |
+------+--------------+
| M4   | [A2, A3]     |
+------+--------------+

2)以下列方式扩展映射。想法是将值分成子集,以便我们可以基于值对键进行分组。例如:[A1, A3]M1中都出现的M2表示[A1, A3]在两个列表中都是相同的。

+------+--------------+-----------------------------+
| Keys | Values       |                             |
+------+--------------+-----------------------------+
| M1   | [A1, A3]     |                             |
+------+--------------+-----------------------------+
| M2   | [A1, A2, A3] | Expanding this entry        |
+------+--------------+ to create a mapping         +
| M2   | [A1, A2]     | of this key i.e. M2 with    |
+------+--------------+ all the possible            +
| M2   | [A1, A3]     | combinations of the         |
+------+--------------+ original value [A1, A2, A3] +
| M2   | [A2, A3]     |                             |
+------+--------------+-----------------------------+
| M3   | [A1, A2]     |                             |
+------+--------------+-----------------------------+
| M4   | [A2, A3]     |                             |
+------+--------------+-----------------------------+

3)反转以上映射,以便对新键执行groupBy。

+--------------+--------+
| Keys         | Values |
+--------------+--------+
| [A1, A3]     | M1     |
+--------------+--------+
| [A1, A2, A3] | M2     |
+--------------+--------+
| [A1, A2]     | M2     |
+--------------+--------+
| [A1, A3]     | M2     |
+--------------+--------+
| [A2, A3]     | M2     |
+--------------+--------+
| [A1, A2]     | M3     |
+--------------+--------+
| [A2, A3]     | M4     |
+--------------+--------+

4)一个以键为公共元素且值作为其对应键的倒置地图。

+--------------+---------+
| Keys         | Values  |
+--------------+---------+
| [A1, A3]     | [M1,M2] |
+--------------+---------+
| [A1, A2, A3] | [M2]    |
+--------------+---------+
| [A1, A2]     | [M2,M3] |
+--------------+---------+
| [A2, A3]     | [M2,M4] |
+--------------+---------+

代码:

然后,下面的代码在第4步中创建上述倒置地图。

Map<List<Integer>, List<String>> invertedMap = inputMap.entrySet().stream()
    .flatMap(test::getAllCombinationsOfAList)
    .collect(Collectors.groupingBy(Entry::getKey,
        Collectors.mapping(Entry::getValue, Collectors.toList())));

其中test::getAllCombinationsOfAList是(带有max 2^list_len iterations):

static Stream<Entry<ArrayList<Integer>, String>> getAllCombinationsOfAList(Entry<String,List<Integer>> set)
{
    int n = set.getValue().size();
    Builder<Entry<ArrayList<Integer>,String>> entryStream = Stream.builder();
    for (int i = 0; i < (1<<n); i++)
    {
        ArrayList<Integer> integers = new ArrayList<>();
        for (int j = 0; j < n; j++) {
            if ((i & (1 << j)) > 0)
                integers.add(set.getValue().get(j));
        }

        if(integers.size() >=2 ) {
            entryStream.accept(new AbstractMap.SimpleEntry<>(integers,set.getKey()));
        }

    }
    return entryStream.build();
}

然后res.entrySet().forEach(System.out::println);的输出是:

[1, 2, 3]=[M2]
[2, 3]=[M2, M4]
[1, 2]=[M2, M3]
[1, 3]=[M1, M2]

在这里,key_list_length表示匹配中应该共有的最小元素数。

value_list_length> = 2表示确实存在匹配项。

因此,key_list_length >= 2value_list_length >= 2将是您想要的输出。例如:

Map<List<Integer>, List<String>> filteredMap = invertedMap.entrySet()
    .stream()
    .filter(e -> e.getKey().size() >=2 && e.getValue().size() >= 2)
    .collect(Collectors.toMap(Entry::getKey,Entry::getValue));

filteredMap.entrySet().forEach(System.out::println);

输出:

[1, 2]=[M2, M3]
[2, 3]=[M2, M4]
[1, 3]=[M1, M2]

答案 4 :(得分:0)

使用Map<String, Set<T>>而不是List。然后您自己的算法就可以发挥作用了。

Set<T> common = new HashSet<>(valuesA);
common.retainAll(valuesB);
if (common.size() > 1) ...

列表将可用于地图,甚至可以用于common,但效率较低,不太适用。