基于 HashMap 更新 List 元素 value/key

Update a List element based on a HashMap value/key

我有 ArrayList:

List<String> wordsList = new ArrayList<>();
// [eu, quero, voltar, para, praia, e, comer, queijo]

和 HashMap:

Map<String, String> wordsMap = new HashMap<>();
//{v.=voltar, c.=comer, q.=queijo., p.=praia}

我想做的是:如果列表元素等于映射值,则用映射 key 替换列表元素。在此示例中,结果将是:

// [eu, quero, v., para, p., e, c., q.]

我试过的是

      for (String word : wordsList) {
         for (Map.Entry<String, String> entry : wordsMap.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (word.equals(value)) {
               newWordsList.add(key);
            } else {
               newWordsList.add(word);
            }
         }
      }
Result: [eu, eu, eu, eu, quero, quero, quero, quero, v., voltar, voltar, voltar, para, para, para, para, praia, praia, praia, p., e, e, e, e, comer, c., comer, comer, queijo, queijo, q., queijo]

有什么帮助吗??谢谢!

给定列表和地图:

List<String> wordsList = Arrays.asList("eu", "quero", "voltar", "para", "praia", "e", "comer", "queijo");
Map<String, String> wordsMap = Map.of("v.", "voltar", "c.", "comer", "q.", "queijo", "p.", "praia");

然后下面的代码 returns 你的结果(没有任何外部依赖):

// If the list element is equal to map value, then replace the list element
// by the map key.In this example, the result would be:

wordsMap.values().stream()
    // If the list contains the map value
    .filter(wordsList::contains)
    .forEach(value -> {
        // Find the corresponding map key
        String mapKey = wordsMap.entrySet()
            .stream()
            .filter(entry -> Objects.equals(entry.getValue(), value))
            .map(Map.Entry::getKey) // It could find several keys if you have the same value for different keys
            .findFirst()
            .orElseThrow(); // It should never happen as we are looping in the map values

        // Find the index and update the list with the map key
        int index = wordsList.indexOf(value);
        wordsList.set(index, mapKey);
    });

System.out.println(wordsList);
// [eu, quero, v., para, p., e, c., q.]

我会使用 google HashBiMap 因为它可以提供同一地图的反向视图,即在 Map<K,V> 中你可以同时执行这两项操作 get(K) returns Vget(V) returns K

        // Form the words list
        List<String> wordsList = new ArrayList<String>();
        wordsList.add("eu");
        wordsList.add("quero");
        // etc adding all of the terms you mentioned

        // Form the keyset
        HashBiMap<String, String> wordsMap = HashBiMap.create();
        wordsMap.put("v", "voltar");
        wordsMap.put("c", "comer");
        // etc adding all the terms you mentioned

        List<String> wordsListOut = new ArrayList<String>();

        // Iterating through the wordsList
        for (int i = 0; i < wordsList.size(); i++) {

            // Get the current element
            String element = wordsList.get(i);
            // Does it have an inverse pair?
            String result = wordsMap.inverse().get(element);

            // If it has an inverse pair, add the inverse key. Otherwise, add the element raw.
            wordsListOut.add(result == null ? element : result);

        }

        // Outputs: [eu, quero, v, para, p, e, c, q]
        System.out.println(wordsListOut);

HashBiMap 的依赖是GoogleGuava:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.1-jre</version>
</dependency>

对我来说,第一步是创建一个使用值作为键并使用键作为值的新映射。这比遍历旧地图并检查值要容易得多。

Map<String, String> invertedWordsMap = wordsMap.entrySet().stream().collect(Collecotrs.toMap(Map.Entry::getValue), Map.Entry::getKey));
//{voltar=v., comer=c., queijo=q.., praia=p.}

然后我只需要检查地图中是否存在密钥

for (int i = 0; i < wordsList.size(); i++) {
    String word = invertedWordsMap.get(wordsList.get(i));
    if (word != null) {
        wordsList.set(i, word);
    }
}

这是强制性的花式流答案。请记住,如果您使用流解决方案,旧列表将被替换而不是更改。

wordsList = wordsList.stream().map(word -> invertedWordsMap.getOrDefault(word, word)).collect(Collectors.toList());

您可以简单地迭代 wordsMap.entrySet(),在 wordsList 中找到 entry#value 的索引,并使用 entry#key 在索引处更新 wordsList .

演示:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        List<String> wordsList = new ArrayList<>(
                List.of("eu", "quero", "voltar", "para", "praia", "e", "comer", "queijo."));
        Map<String, String> wordsMap = Map.of("v.", "voltar", "c.", "comer", "q.", "queijo.", "p.", "praia");

        for (Map.Entry<String, String> entry : wordsMap.entrySet()) {
            int index = wordsList.indexOf(entry.getValue());
            if (index != -1) {
                wordsList.set(index, entry.getKey());
            }
        }

        System.out.println(wordsList);
    }
}

输出:

[eu, quero, v., para, p., e, c., q.]

注意: List#indexOf returns 指定元素在这个列表中第一次出现的索引,或者 -1 如果这个列表不包含该元素。因此,上面给出的解决方案中的逻辑会检查 -1,并且只会替换第一次出现的匹配值。如果你的列表有重复的元素并且你想要替换所有出现的匹配值,你需要找到匹配值的所有索引并替换它们,如下所示:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        List<String> wordsList = new ArrayList<>(List.of("eu", "quero", "queijo.", "voltar", "queijo.", "para", "praia",
                "e", "queijo.", "comer", "queijo."));
        Map<String, String> wordsMap = Map.of("v.", "voltar", "c.", "comer", "q.", "queijo.", "p.", "praia");
        
        for (Map.Entry<String, String> entry : wordsMap.entrySet()) {
            List<Integer> allIndices = allIndicesOf(wordsList, entry.getValue());
            for (int index : allIndices) {
                wordsList.set(index, entry.getKey());
            }
        }

        System.out.println(wordsList);
    }

    static List<Integer> allIndicesOf(List<String> list, String str) {
        return IntStream.range(0, list.size())
                .boxed()
                .filter(i -> list.get(i).equals(str))
                .collect(Collectors.toList());
    }
}

输出:

[eu, quero, q., v., q., para, p., e, q., c., q.]