2014-10-28 2 views
1

Я работаю над заданием для класса Computer Science 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()); 
    } 

Я работал над этим в своей среде IDE NetBeans и следил за некоторыми предложениями. Это изменило два последних для петель:

chars.stream().filter((char1) -> (!prob.contains(char1))).forEach((char1) -> { 
     prob.add(char1); 
    });  

    prob.stream().map((prob1) -> { 
     System.out.print("Frequency of " + prob1); 
     return prob1; 
    }).forEach((prob1) -> { 
     System.out.println(": " + ((double) Collections.frequency(chars, prob1))/chars.size()); 
    }); 

Я действительно, действительно, действительно заинтригован этим, но я нахожу это трудно отследить все. Очевидно, что он работает так же, как и мой цикл, и после тестирования я вижу, что это не работает, но я хочу понять, почему и как. Может ли кто-нибудь дать мне представление?

+1

Это потоки Java 8 с использованием лямбда-выражений –

ответ

1

Netbeans сделал все возможное, чтобы реорганизовать ваш код, чтобы использовать java 8 потоков, но на самом деле это можно сделать намного лучше. Например, похоже, что проблема должна содержать отдельный список символов. В Java 8, вы можете сделать это следующим образом:

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

Но все, что вы используете для Prob является то подсчитать, сколько раз появляется каждый символ символов. С потоками, вы можете сделать это без первого составления списка ВЕРОЯТНОСТЬ:

Map<Character, Long> freq = chars.stream() 
    .collect(
     Collectors.groupingBy(
      x->x, 
      Collectors.counting() 
     ) 
    ); 

Статические методы в классе Collections, как правило, только что импортировали статически, так что выше было бы написано как:

Map<Character, Long> freq = chars.stream() 
    .collect(groupingBy(x->x, counting()); 

Это означает, что , возьмите мой поток символов и сделайте карту. Ключом карты является сам символ (это то, что x-> x делает), а значение карты - это счет того, сколько раз этот символ встречается в символах.

Но это еще не все! Первая половина вашего метода проходит по строкам файла и собирает символы. Это может быть переписано с потоками, а также:

Stream<Character> charStream = Files.lines(Paths.get(filename)) 
     .flatMap(line -> line.chars().mapToObj(i->(char) i)); 

File.lines (..) дает нам поток линий. Часть flatMap является немного загадочной, но она разворачивает каждую строку в поток отдельных символов и объединяет потоки, так что у нас есть один большой поток символов.

А теперь мы соберем все вместе:

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) 
    ); 

} 

Edit:

Для вывода частот в отсортированном порядке, это сделать (с помощью import static java.util.Comparator.*):

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)); 

Мы принимаем отображение символа для подсчета, поток его записей, сортировка их по значениям в обратном порядке и печать каждого из них.

+0

Святое дерьмо, это потрясающе! Как бы я сортировать его по частоте? – JonMcDev

+0

@JonMcDev см. Редактирование – Misha

2

Ваша IDE заменила часть вашего кода новыми функциями Java 8 - потоками и лямбда-выражениями. Вы должны прочитать о них.

Потоки позволяют выполнять операции над коллекцией в конвейере, где только конечная (терминальная) операция выполняет фактическую итерацию по элементам (всего лишь столько элементов, сколько требуется).

Лямбда-выражения позволяют писать меньше кода при передаче анонимных экземпляров класса, реализующих функциональные интерфейсы (= интерфейсы с одним методом) методам.

Вот попытка объяснить, что новый код делает:

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()); 
    }); 

map операция на втором потоке немного странно. Обычно карта используется для преобразования потока одного типа в поток другого типа. Здесь он используется для вывода вывода и возвращает тот же поток. Я бы не использовал map. Вы можете просто переместить печать на forEach.

prob.stream() // creates a Stream<Character> of the prob List 
    .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()); 
    }); 

На самом деле, вам не нужен поток для этого, так как коллекции также есть метод forEach в Java 8:

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()); 
    }); 
0

мне это выглядит как NetBeans переработан код для использования Java 8-х лямбда или функционального программирования с использованием карты - уменьшить из интерфейса Stream.

Для получения дополнительной информации о карте()/уменьшения()/интерфейс потока см to this link

Пожалуйста, ознакомьтесь с предложениями о том, что IDE обеспечивает, прежде чем применять их :)

0

Во-первых, вы должны прочитать о java.util.Stream пакет, чтобы получить первое впечатление о том, как API разработан и для каких пурпусов.

Вот что делает ваш первый цикл, в виде слова:

  • перебирать значения от 0 до chars.size() - 1 и добавить соответствующий элемент из chars в prob, но только если это уже нет.

с потоком API добавлен в Java с Java 8 такие задачи могут быть написаны в функциональном стиле программирования, который фокусируется на «как это делается» не на «с тем, что IST сделать ".

chars.stream() 
    .filter(char1 -> !prob.contains(char1)) 
    .forEach(char1 -> { 
     prob.add(char1); 
    });  
  • ArrayList реализует Collection<T> и поэтому метод stream().
  • Этот поток (все элементы из коллекции в трубопроводе) фильтруется (ваш бывший Условный оператор)
  • На потоке остальных элементов, выполнить последнюю операцию prop.add

Это может быть немного слишком много сейчас, но вы можете изменить последнюю операцию (.forEach) будет еще яснее:

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

для лучшего понимания или debuggin целей вы можете найти Stream#peek интересным которые позволяют.

Смежные вопросы