Java For循环替换:For循环到过滤器

Java For循环替换:For循环到过滤器,java,for-loop,filter,stream,file-processing,Java,For Loop,Filter,Stream,File Processing,我正在为计算机科学III课程(Java编程)做作业,其中我们必须基于哈夫曼编码对文件进行编码 import java.util.Scanner; import java.util.ArrayList; import java.io.*; import java.util.Collections; import java.util.StringTokenizer; public class Client { public static void main(String[] args) thro

我正在为计算机科学III课程(Java编程)做作业,其中我们必须基于哈夫曼编码对文件进行编码

import java.util.Scanner;
import java.util.ArrayList;
import java.io.*;
import java.util.Collections;
import java.util.StringTokenizer;

public class Client {

public static void main(String[] args) throws IOException {
    // TODO code application logic here
    Scanner in = new Scanner(System.in);
    System.out.println("Enter a filename to read from.");
    String filename = in.nextLine();
    File file = new File(filename);
    Scanner inputFile = new Scanner(file);
    String line, word;
    StringTokenizer token;
    ArrayList<Character> chars = new ArrayList<>();
    while(inputFile.hasNext()){
        line = inputFile.nextLine();
        ArrayList<Character> lineChar = new ArrayList<>();
        for (int i=0; i<line.length(); i++){
            if (line.charAt(i)!=' '){
                lineChar.add(line.charAt(i));
            }
        }
        chars.addAll(lineChar);
    }

    ArrayList<Character> prob = new ArrayList<Character>();
    for (int i=0; i<chars.size(); i++){
        if (!prob.contains(chars.get(i))){
            prob.add(chars.get(i));
        }
    }    
    for (int i=0; i<prob.size(); i++){
        System.out.print("Frequency of " + prob.get(i));
        System.out.println(": " + ((double)Collections.frequency(chars, prob.get(i)))/chars.size());
    }

我真的,真的,真的对这很感兴趣,但我发现很难追踪所有的东西。显然,它的运行方式与for循环相同,测试后我发现它确实有效,但我想了解原因和方法。有人能给我一些见解吗?

您的IDE用新的Java 8特性(流和lambda表达式)替换了一些代码。你应该读一下

流允许您对管道中的集合执行操作,其中只有最终(终端)操作对元素进行实际迭代(只针对所需的元素)

Lambda表达式允许您在将实现函数接口(=具有单个方法的接口)的匿名类实例传递给方法时编写更少的代码

下面尝试解释新代码的作用:

chars.stream() // creates a Stream<Character> from your chars List
     .filter((char1) -> (!prob.contains(char1))) // keeps only Characters not contained
                                                 // in prob List
     .forEach((char1) -> {prob.add(char1);}); // iterates over all the elements of
                                              // the Stream (i.e. those that weren't
                                              // filtered out) and adds them to prob   

prob.stream() // creates a Stream<Character> of the prob List
    .map((prob1) -> {
      System.out.print("Frequency of " + prob1);
      return prob1;
     }) // prints "Frequency of " + character for the current Character in the Stream
    .forEach((prob1) -> { // prints the frequency of each character in the Stream
        System.out.println(": " + ((double) Collections.frequency(chars, prob1)) /  chars.size());
     });
实际上,这不需要流,因为集合在Java 8中也有一个
forEach
方法:

prob.forEach((prob1) -> { // prints the frequency of each character in the Stream
        System.out.print("Frequency of " + prob1);
        System.out.println(": " + ((double) Collections.frequency(chars, prob1)) /  chars.size());
     });

在我看来,NetBeans重构了您的代码,以使用Java8的lambda或使用流接口中的map-reduce的函数式编程操作

有关map()/reduce()/stream接口的更多信息,请参阅


请先阅读IDE提供的建议,然后再应用它们:)

首先,您应该阅读有关该软件包的内容,以便对API的设计以及目的有一个第一印象

以下是您的第一个循环以word形式执行的操作:

  • 迭代从0到chars.size()-1的值,并将相应元素从
    chars
    添加到
    prob
    ,但前提是该元素尚未存在
通过使用Java8将流API添加到Java中,这样的任务可以以函数式编程风格编写,其重点是“如何完成”,而不是“完成了什么”

  • ArrayList
    实现
    Collection
    ,从而实现方法
  • 此流(管道中集合中的所有元素)正在被筛选(通过您以前的if语句)
  • 在剩余元素的流上,执行最后的操作
    prop.add
现在这可能有点太多了,但您可以将最后一个操作(.forEach)更改为更清晰:

//...
.forEach(prop::add);

为了更好地理解或调试,您可能会发现有趣的方法。

Netbeans尽其所能重构代码以使用java 8流,但实际上可以做得更好。例如,prob似乎应该包含一个不同的字符列表。在java 8中,您可以这样做:

List<Character> prob = chars.stream()
    .distinct()
    .collect(Collectors.toList());

我们使用字符映射进行计数,对其条目进行流式处理,按照值的相反顺序对它们进行排序,然后将每个条目打印出来。

这些是使用lambda表达式的Java 8流。真是太棒了!我该如何根据频率对其进行排序?
chars.stream()
    .filter(char1 -> !prob.contains(char1))
    .forEach(char1 -> {
        prob.add(char1);
    });    
//...
.forEach(prop::add);
List<Character> prob = chars.stream()
    .distinct()
    .collect(Collectors.toList());
Map<Character, Long> freq = chars.stream()
   .collect(
        Collectors.groupingBy(
            x->x, 
            Collectors.counting()
        )
   );
Map<Character, Long> freq = chars.stream()
   .collect(groupingBy(x->x, counting());
Stream<Character> charStream = Files.lines(Paths.get(filename))
        .flatMap(line -> line.chars().mapToObj(i->(char) i));
public static void main(String[] args) throws IOException {

    Scanner in = new Scanner(System.in);
    System.out.println("Enter a filename to read from.");
    String filename = in.nextLine();

    Map<Character, Long> freq = Files.lines(Paths.get(filename))
            .flatMap(line -> line.chars().mapToObj(i -> (char) i))
            .collect(groupingBy(x -> x, counting()));

    long total = freq.values().stream().mapToLong(x->x).sum();

    freq.forEach((chr, count) ->
            System.out.format("Frequency of %s: %s%n", chr, ((double) count) / total)
    );

}
freq.entrySet().stream()
        .sorted(comparing(e->e.getValue(), reverseOrder()))
        .forEach(e -> System.out.format("Frequency of %s: %s%n", e.getKey(), (double) e.getValue() / total));