【问题标题】:Mapping letters in a string to number of occurences, using Stream [duplicate]使用Stream将字符串中的字母映射到出现次数
【发布时间】:2021-07-12 10:48:19
【问题描述】:

我有一个字符串 s,我正在尝试使用 Stream API 将 s 中的每个字母映射到它在 s 中出现的次数。
例如,giraffe -> {1, 1, 1, 1, 2, 2, 1}
我尝试使用的代码是:

String s = "giraffe";
int[] occurences = s.chars().map(c -> count(s, c)).toArray();

public int count(String text, char ch) {
    return (int) text.chars().filter(c -> c == ch).count();
}

但我在以int[] occurences = ... 开头的行上得到incompatible types: possible lossy conversion from int to char 我尝试了一些不同的变化,但没有任何效果。有任何想法吗?谢谢

【问题讨论】:

    标签: java lambda java-stream


    【解决方案1】:

    问题

    问题的真正原因是没有任何CharStream 类。出于这个原因,s.chars() 给你一个IntStream。因此,在map(c -> count(s, c)) 中,c 的类型为int。因此,您尝试将int 传递给您的count 方法,它需要char。这会给你你的错误信息。 int 可以转换为char,但是会丢弃一些位,这就是转换必然有损的原因。

    两个明显的解决方案

    解决方案 1: 告诉 Java 您打算进行这种有损转换。由于您知道 c 来自字符串中的 char,因此再次将其转换回 char 不会造成任何伤害。

        int[] occurences = s.chars().map(c -> count(s, (char) c)).toArray();
    

    解决方案 2:

    你可以声明你的方法来接受int

    public int count(String text, int ch) {
    

    现在不需要转换(转换)。比较c == ch 仍然可以正常工作。无论如何,它正在比较两个ints,所以不会造成任何伤害。

    PS 在其他答案中还有其他很好的解决方案和改进。就个人而言,我很想将字符串预处理为计数映射,因此我不需要为每个字母再次计数。这不是必需的,您的代码只需使用提到的两个更改之一即可正常工作。

    【讨论】:

    • 真棒,详细的答案。谢谢,我没有意识到问题是由于 c 被转换为 int
    【解决方案2】:

    我已修改您的 count 方法以接受 int 而不是 char 并且它有效

    import java.util.Arrays;
    class Main {
      public static void main(String[] args) {
        
        String s = "giraffe";
    int[] occurences = s.chars().map(c -> count(s, c)).toArray();
    
     System.out.println(Arrays.toString(occurences));
    
    
      }
      public static int count(String text, int ch) {
        return (int) text.chars().filter(c -> c == ch).count();
    }
    }
    

    repl

    【讨论】:

      【解决方案3】:

      有很多方法可以做到这一点。其中一种方法是使用\X 作为指定any Unicode extended grapheme cluster 的正则表达式,并按捕获的Matcher 组进行分组。

      演示:

      import java.util.LinkedHashMap;
      import java.util.Map;
      import java.util.regex.MatchResult;
      import java.util.regex.Pattern;
      import java.util.stream.Collectors;
      import java.util.stream.Stream;
      
      public class Main {
          public static void main(String[] args) {
              // Test
              Stream.of(
                          "giraffe", 
                          "He?llo"
                      ).forEach(s -> System.out.println(getFrequencyMap(s)));
          }
      
          static Map<Object, Long> getFrequencyMap(String s) {
              return Pattern.compile("\\X").matcher(s).results()
                      .collect(Collectors.groupingBy(MatchResult::group, LinkedHashMap::new, Collectors.counting()));
          }
      }
      

      输出:

      {g=1, i=1, r=1, a=1, f=2, e=1}
      {H=1, e=1, ?=1, l=2, o=1}
      

      【讨论】:

        【解决方案4】:

        使用循环,我会这样做。

        • 分配一个数组来保存所有字符
        • 然后只打印非零的那些
        int[] count = new int[256]; // default to all 0's
        for (char c : s.toCharArray()) {
            count[c]++;  // use the character to index the array and update the count
        }
        
        for (int i = 0; i < count.size; i++) {
           if (count[i] > 0) {
              System.out.println((char)i + " " + count[i]);
            }
        }
        

        您还可以使用流转换为地图。顺序将不同,但频率计数将相同。它的工作原理如下:

        • 拆分字符串并流式传输字符。
        • 按字符分组
        • 并在找到键时应用计数收集器来更新计数。
        String s = "giraffe";
        Map<String, Long> freq = Arrays.stream(s.split("")).collect(
                Collectors.groupingBy(c -> c, Collectors.counting()));
        
        freq.entrySet().forEach(System.out::println);
        

        打印

        
        a=1
        r=1
        e=1
        f=2
        g=1
        i=1
        

        【讨论】:

          【解决方案5】:

          抱歉,我认为Streams 不适合这个。最简单的方法是使用Map:

          public static Map<Character, Integer> histogram(String str) {
              Map<Character, Integer> map = new TreeMap<>();
          
              for (int i = 0; i < str.length(); i++) {
                  char ch = Character.toLowerCase(str.charAt(i));
                  map.put(ch, map.getOrDefault(ch, 0) + 1);
              }
          
              return map;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-10-18
            • 1970-01-01
            • 2012-01-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多