使用流java8基于条件修改HashMap

时间:2018-06-12 06:45:43

标签: java java-8 hashmap java-stream

我想通过对Map的键应用一些操作,从现有的HashMap<String,Integer>创建HashMap<String,Integer>。 假设我有一个String-&gt;

String sampleString= "SOSSQRSOP";` 

然后通过从下面的字符串中只取3个字符(将0作为值)来创建一个hashmap:

Map<String, Integer> messages= new HashMap<>();
messages.put("SOS",0); 
messages.put("SQR",0);
messages.put("SOP",0);

实际任务是使用地图中的每个键从给定字符串“SOS”中查找不同字符的总数,并将no指定给每个键的值。 如下(最终结果):

Map<String, Integer> messages= new HashMap<>();
messages.put("SOS",0);
messages.put("SQR",2);
messages.put("SOP",1);

所以我使用下面给出的流在java8中编写代码:

    Map<String,Integer>  result= messages
            .entrySet().stream()
            .collect(Collectors.toMap(e-> e.getKey(),
                    e-> e.getKey().stream()
                         .forEach(x-> {
                                if(!"SOS".equals(x)){
                                    char[] characters= {'S','O','S'};
                                    char[] message= x.toCharArray();
                                    for(int i=0; i< characters.length;i++){
                                        int index=0;
                                        if(characters[i] != message[i]){
                                            messages.put(e.getKey(),++index);
                                        }
                                    }
                                }
                            });
                    ));

我收到编译错误。任何人都可以帮我用流编写代码。

编辑:也请描述其他方法来做到这一点。在我的情况下,BTW需要从给定的字符串创建第一个hashmap。

5 个答案:

答案 0 :(得分:5)

无需预先制作HashMap。流收集器toMap将为您提供:

import static java.util.stream.Collectors.toMap;

Map<String, Integer> result = Stream.of("SOS", "SQR", "SOP")
        .collect(toMap(
                s -> s,
                s -> (int) IntStream.range(0, 3)
                        .filter(i -> "SOS".charAt(i) != s.charAt(i))  // assume all words are 3-letters
                        .count()
         ));

但是,如果您已有地图并想要修改它,请使用replaceAll

messages.replaceAll(
        (s, unused) -> (int) IntStream.range(0, 3)
                .filter(i -> "SOS".charAt(i) != s.charAt(i))
                .count()
);

如果您的任务是拆分源消息并将每个三元组与前三个字符进行比较,则可以将它们全部合并到一个流表达式中:

String message = "SOSSQRSOP";
int n = 3;

assert message.length() % n == 0;

Map<String, Integer> messages = IntStream.range(0, message.length() / n)
        .map(i -> i * n) // starting points of the n-grams
        .mapToObj(idx -> message.substring(idx, idx + n))
        .collect(toMap(
                group -> group,
                group -> (int) IntStream.range(0, n)
                        .filter(i -> message.charAt(i) != group.charAt(i))
                        .count()
        ));

答案 1 :(得分:4)

您无法在值映射器中使用forEach,因为它不会返回值。

String x = "SOS";
Map<String,Integer> result = messages
        .entrySet().stream()
        .collect(Collectors.toMap(e-> e.getKey(),
                                  e-> {
                                          int count = 0;
                                          for (int i = 0; i < characters.length; i++){
                                              if (e.getKey().charAt(i) != x.charAt(i)) {
                                                  count++;
                                              }
                                          }
                                          return count;
                                      }));

答案 2 :(得分:3)

您可以做的最好的事情IMO根本没有使用Stream,但replaceAll

Map<String, Integer> messages = new HashMap<>();
// sample entries
messages.put("SOS", 0);
messages.put("SQR", 0);
messages.put("SOP", 0);

messages.replaceAll((k, v) -> {
    // calculate new value for each entry
    int diff = 0;
    for (int i = 0; i < "SOS".length(); i++) {
        if ("SOS".charAt(i) != k.charAt(i)) {
            diff++;
        }
    }
    return diff;
});
System.out.println(messages);

输出

  

{SQR = 2,SOP = 1,SOS = 0}

答案 3 :(得分:1)

我假设你输入的是一个包含所有字符的字符串。 因此,您可以跳过初始映射的创建,并将字符串拆分为3个块,将每个结果String映射到一个键,并使用一些公式计算值的差异。

    String sampleString= "SOSSQRSOP";
    final char[] SOS = "SOS".toCharArray();
    Map<String, Integer> result = IntStream.rangeClosed(0, sampleString.length() / 3)
                                           .mapToObj(i -> sampleString.substring(3*i, Math.min(3*i+3, sampleString.length())))
                                           .filter(s -> !s.isEmpty())
                                           .collect(Collectors.toMap(s -> s,
                                                                     s -> (int) IntStream.range(0, s.length())
                                                                                         .filter(i -> SOS[i] != s.charAt(i))  
                                                                                         .count()));

    result.forEach((k,v) -> System.out.println(k + " -> " + v));

关于您自己的实施:

for(int i=0; i< characters.length;i++){
   int index=0; //<-- this will always set the index to 0
   if(characters[i] != message[i]){
     messages.put(e.getKey(),++index); //<-- this sets the index to one before assigning it to the map entry value, resulting in being 1, always
   }
}

答案 4 :(得分:0)

您这样做:e.getKey().stream(),这意味着您尝试流式传输单个值,因为您的密钥是字符串,这是不可能的。

您可以e.getKey().chars().forEach(...);获得预期结果,也可以e.codePoints().forEach(...)

如果您使用上述方法之一,则还需要将条目转换为字符,因为codePoints()chars()会返回IntStream。

正如@Holger在评论中提到的那样,我的第一个答案包含以下代码段Stream.of(e.getKey().toCharArray()),它会为您提供一个包含char[]的流。

当然,有更好的方法可以做你想做的事,但你的问题是编译错误。