如何统计字符串中字符的出现频率?

36

我需要写一个循环来统计字符串中每个字母的出现频率。
例如:"aasjjikkk" 将会有 2 个 'a',1 个 's',2 个 'j',1 个 'i',3 个 'k'。最终,我希望这些数据以字母为键、出现次数为值的形式被存储在一个 map 中。有好的想法如何实现吗?


你可以在这个重复的问题上找到更多好的答案:如何将字符流转换为Map<Character, Integer> - undefined
27个回答

36
您可以使用Java Map将一个字符映射为整数。然后,您可以迭代字符串中的字符,并检查它们是否已添加到Map中。如果是,则可以将其值递增。

例如:

Map<Character, Integer> map = new HashMap<Character, Integer>();
String s = "aasjjikkk";
for (int i = 0; i < s.length(); i++) {
    char c = s.charAt(i);
    Integer val = map.get(c);
    if (val != null) {
        map.put(c, val + 1);
    }
    else {
       map.put(c, 1);
   }
}

最终,您将拥有遇到的所有字符计数,并可以从中提取它们的频率。

或者,您可以使用Bozho的解决方案,使用Multiset并计算总出现次数。


2
哦,但你不能实例化Map,它是抽象的,只是提醒您。 - Bill
我的错,应该是HashMap,而不是Map。感谢你的指出。 - xunil154
<char, int> 应该改为 <Character, Integer>。 - Marcelo
HashMap<char... 应该不起作用,对吧?HashMap 要求其泛型类型为引用类型而不是立即数,对吧?你可以在这里使用装箱的 Character,或者你可以看一下我的数组解决方案。 - Bernd Elkemann
请问您能解释一下这个步骤吗:如果(val!= null){...} - Christos Michael
@ChristosMichael 如果值为null,即没有该字符的条目,则应将其增加到1,因为这是第一次出现。 - User

21
使用JDK-8的流API:
Map<Character, Long> frequency =
            str.chars()
               .mapToObj(c -> (char)c)
               .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

或者如果您希望将值作为整数(Integer)返回:
Map<Character, Integer> frequency =
            str.chars()
               .mapToObj(c -> (char)c)
               .collect(Collectors.groupingBy(Function.identity(), Collectors.summingInt(c -> 1)));

另一种变体:
Map<Character, Integer> frequency = 
            str.chars()
               .mapToObj(c -> (char)c)
               .collect(Collectors.toMap(Function.identity(), c -> 1, Math::addExact));

19

一个简洁的方法是:

Map<Character,Integer> frequencies = new HashMap<>();
for (char ch : input.toCharArray()) 
   frequencies.put(ch, frequencies.getOrDefault(ch, 0) + 1);

我们使用for-each循环遍历每个字符。 frequencies.getOrDefault() 如果键存在,则获取其值,否则返回(默认情况下)第二个参数。


1
更加简洁的写法是:frequencies.merge(ch, 1, Integer::sum); 这样也不需要进行两次地图查找。 - undefined

14

你可以使用Multiset(来自于guava)库。它将为你提供每个对象的计数。例如:

Multiset<Character> chars = HashMultiset.create();
for (int i = 0; i < string.length(); i++) {
    chars.add(string.charAt(i));
}

然后对于每个字符,您可以调用chars.count('a'),它会返回出现次数


老实说,我需要一些标准库里的东西。 - Bill

4

这里有另一个方案,虽然可能不太可靠。

public char getNumChar(String s) {
    char[] c = s.toCharArray();
    String alphabet = "abcdefghijklmnopqrstuvwxyz";
    int[] countArray = new int[26];
    for (char x : c) {
        for (int i = 0; i < alphabet.length(); i++) {
            if (alphabet.charAt(i) == x) {
                countArray[i]++;
            }
        }
    }

    java.util.HashMap<Integer, Character> countList = new java.util.HashMap<Integer, Character>();

    for (int i = 0; i < 26; i++) {
        countList.put(countArray[i], alphabet.charAt(i));
    }
    java.util.Arrays.sort(countArray);
    int max = countArray[25];
    return countList.get(max);
}

1
不必使用两个嵌套循环,你可以简单地使用countArray[i - 'a']++;。而且,为了找到数组的最大值而进行排序是相当低效的。你可以通过一个简单的线性循环找到最大值,并且在这样做的同时,你也知道它的索引对应的字母,使得HashMap变得不再必要。 - undefined

4

因为没有Java 8的解决方案,所以考虑发布一个。此外,这个解决方案比其他一些提到的解决方案更加整洁、可读、简洁。

String string = "aasjjikkk";

Map<Character, Long> characterFrequency = string.chars()  // creates an IntStream
    .mapToObj(c -> (char) c) // converts the IntStream to Stream<Character>
    .collect(Collectors.groupingBy(c -> c, Collectors.counting())); // creates a
                                                                    // Map<Character, Long> 
                                                                    // where the Long is
                                                                    // the frequency

3
好的,有两种方法,具体取决于您的喜好:
  1. 按字符对数组进行排序。然后,计算每个字符变得微不足道。但是您需要先复制一份数组。
  2. 创建另一个大小为26的整数数组(称为freq),str是字符数组。
  3. for(int i = 0; i < str.length; i ++)

    freq[str[i] - 'a'] ++; //假设所有字符都是小写

所以'a'的数量将存储在freq [0]中,而'z'的数量将存储在freq [25]中。

2

这里有一个解决方案:

定义您自己的 Pair

public class Pair
{
    private char letter;
    private int count;
    public Pair(char letter, int count)
    {
        this.letter = letter;
        this.count= count;
    }
    public char getLetter(){return key;}
    public int getCount(){return count;}
}

那么你可以这样做:

public static Pair countCharFreq(String s)
{
    String temp = s;
    java.util.List<Pair> list = new java.util.ArrayList<Pair>();
    while(temp.length() != 0)
    {
        list.add(new Pair(temp.charAt(0), countOccurrences(temp, temp.charAt(0))));
        temp.replaceAll("[" + temp.charAt(0) +"]","");
    }
}

public static int countOccurrences(String s, char c)
{
    int count = 0;
    for(int i = 0; i < s.length(); i++)
    {
        if(s.charAt(i) == c) count++;
    }
    return count;
}

2

您可以使用来自Eclipse CollectionsCharAdapterCharBag,避免将数据装箱为CharacterInteger

CharBag bag = Strings.asChars("aasjjikkk").toBag();

Assert.assertEquals(2, bag.occurrencesOf('a'));
Assert.assertEquals(1, bag.occurrencesOf('s'));
Assert.assertEquals(2, bag.occurrencesOf('j'));
Assert.assertEquals(1, bag.occurrencesOf('i'));
Assert.assertEquals(3, bag.occurrencesOf('k'));

注意:我是 Eclipse Collections 的提交者。

2
String s = "aaaabbbbcccddddd";
Map<Character, Integer> map = new HashMap<>();

使用Java8中的一行代码
s.chars().forEach(e->map.put((char)e, map.getOrDefault((char)e, 0) + 1));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接