Java 使用流、无映射、无键值对计算和打印文本中的字符频率

Java 使用流、无映射、无键值对计算和打印文本中的字符频率,java,functional-programming,java-stream,Java,Functional Programming,Java Stream,我试图计算并打印文本中所有字符的频率。 我想使用数组或数组列表执行此操作,并且不使用带有键值对的映射 下面的代码给出了首选结果。 我想摆脱main的for循环 并使用方法countlets()完成所有工作 为了清楚起见,我希望以一种功能性的方式来实现这一点,而不使用任何for循环或if语句。 这能做到吗?如果是,怎么做 公开信柜4{ publicstaticvoidmain(字符串[]a){ 系统输出打印(“输入文本>”; int[]res=countLetters(); for(int i=

我试图计算并打印文本中所有字符的频率。 我想使用
数组
数组列表
执行此操作,并且不使用带有键值对的映射

下面的代码给出了首选结果。 我想摆脱
main的
for
循环 并使用方法
countlets()
完成所有工作

为了清楚起见,我希望以一种功能性的方式来实现这一点,而不使用任何for循环或if语句。 这能做到吗?如果是,怎么做

公开信柜4{

publicstaticvoidmain(字符串[]a){
系统输出打印(“输入文本>”;
int[]res=countLetters();
for(int i=0;is.charAt(0))
.filter(字符::isleter)
收集(
()->新的ArrayList(Collections.nCopies(26,0)),
(李,埃尔)->{
整数oInt=li.get(el-'a');
li.set(el-'a',++oInt);
},
(结果1,结果2)->{
对于(int i=0;i

下面是示例代码,希望这是您要问的问题

    import java.util.Arrays;
import java.util.Scanner;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class LetterCounter4 {


    public static void main(String[] a) {

    System.out.print("Input text > ");


   countLetters();

}

private static void countLetters() {
    try{
        Arrays.stream(new Scanner(System.in).nextLine().toLowerCase()
                .split(""))
     .flatMap(line -> Stream.of(line.split("\\s+")))
     .map(String::toLowerCase)
     .collect(Collectors.toMap(word -> word, word -> 1, Integer::sum))
     .entrySet()
     .stream()
     .sorted((a, b) -> a.getValue() == b.getValue() ? a.getKey().compareTo(b.getKey()) : b.getValue() - a.getValue())
     .forEach(System.out::println);
    }catch(Exception e){
        e.printStackTrace();
    }

}
}

最简单的方法是使用
.groupingBy(c->c,Collectors.counting())
。代码会更简单,当有人输入À或[a-zA-Z]范围之外的其他字母时,代码不会爆炸。但这会创建一个
地图
,你说你不想要它

如果您特别希望坚持使用数组和列表,可以采用以下方法:

public static void main(String[] args) {
    String input = new Scanner(System.in).nextLine();

    int[] counts = countLetters(input);

    IntStream.range(0, counts.length)
            .filter(i -> counts[i] > 0)
            .forEachOrdered(i -> System.out.printf("%c appears %s %s%n", 
                    'a' + i, 
                    counts[i], 
                    counts[i] > 1 ? "times" : "time"
            ));
}

public static int[] countLetters(String s) {
    return s.chars() // this is better than stream(split(""))
            .filter(Character::isLetter)  // WRONG to assume that all letters are [a-zA-Z]
            .map(chr -> Character.toLowerCase(chr) - 'a')
            .collect(
                    () -> new int[26], 
                    (ary, i) -> ary[i]++, 
                    (a,b) -> Arrays.setAll(a, i -> a[i] + b[i])
            );
}

那么它只会打印列表的值,不是吗?但我无法将每个值都链接到相应的字符。除非我能知道我在每个步骤中打印的列表的索引。谢谢你的回复。这确实符合我的要求。但我不想使用for循环和if语句。只有函数式编程。如果您希望“空格”(“”)也被计算,则意味着从拆分函数中删除“\\s+”,这确实符合我的要求。collect的组合器可以由(a,b)->{for(inti=0;ia[i]+=b[i]作为setAll使用的函数接口中方法的主体。为什么只需将int i作为参数传递,而不必传递i、a、b?我希望函数接口要求3个参数。为了知道它必须使用的数组。@xtra抱歉,这是一个输入错误(尽管代码仍然有效)。它应该是
i->a[i]+b[i]
i
被显式传递。
a
b
是从封闭范围中捕获的。当您说“错误地假设所有字母都是[a-zA-Z]”时,您不应该通过收集到长度为
26
的固定大小数组中来犯同样的错误……或者,过滤
[a-zA-Z]
或收集到支持其他字符的内容中。当支持所有unicode字母时,返回数组不是最佳选择…
public static void main(String[] args) {
    String input = new Scanner(System.in).nextLine();

    int[] counts = countLetters(input);

    IntStream.range(0, counts.length)
            .filter(i -> counts[i] > 0)
            .forEachOrdered(i -> System.out.printf("%c appears %s %s%n", 
                    'a' + i, 
                    counts[i], 
                    counts[i] > 1 ? "times" : "time"
            ));
}

public static int[] countLetters(String s) {
    return s.chars() // this is better than stream(split(""))
            .filter(Character::isLetter)  // WRONG to assume that all letters are [a-zA-Z]
            .map(chr -> Character.toLowerCase(chr) - 'a')
            .collect(
                    () -> new int[26], 
                    (ary, i) -> ary[i]++, 
                    (a,b) -> Arrays.setAll(a, i -> a[i] + b[i])
            );
}