Приращение значений хэш-карты для частоты ключа не печатается

#java #arraylist #hashmap #frequency

#java #arraylist #hashmap #частота

Вопрос:

В настоящее время я работаю над проектом по подсчету частоты слов в текстовом файле. Программа-драйвер помещает слова в ArrayList (после того, как делает их строчными и удаляет пробелы), а затем объект FreqCount помещает ArrayList в хэш-карту, которая будет обрабатывать частотные операции. Пока я могу заставить драйвер прочитать текстовый файл, поместить его в ArrayList, а затем поместить его в HashMap. Моя проблема в том, что узлы HashMap не повторяются, поэтому я пытаюсь увеличивать значение каждый раз, когда отображается слово.

Драйвер:

 package threetenProg3;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.ArrayList;


public class Driver {
    
    public static void main(String[] args) throws FileNotFoundException{
        File in = new File("test.txt");
        Scanner scanFile = new Scanner(in);
        
        ArrayList<String> parsed = new ArrayList<String>();
        
        
        while(scanFile.hasNext()) { //if this ends up cutting off bottom line, make it a do while loop
            parsed.add(scanFile.next().toLowerCase());
        }
        
        for(int i = parsed.size()-1; i>=0; i--) { //prints arraylist backwards
            System.out.println(parsed.get(i));
        } //*/
        
        FreqCount fc = new FreqCount(parsed);
        
        System.out.println("n Hashmap: n");
        fc.printMap();
        
        scanFile.close();
        
    }
}
  

FreqCount:

 package threetenProg3;

import java.util.HashMap;
import java.util.List;

public class FreqCount {
    //attributes and initializations
    private HashMap<String, Integer> map = new HashMap<String, Integer>();
    
    //constructors
    
    FreqCount(List<String> driverList){
        for(int dLIndex = driverList.size()-1; dLIndex>=0; dLIndex--) { //puts list into hashmap
            
            for(String mapKey : map.keySet()) {
                
                if(mapKey.equals(driverList.get(dLIndex))) {
                    int tval = map.get(mapKey);
                    
                    map.remove(mapKey);
                    
                    map.put(mapKey, tval 1);
                }else {
                    map.put(mapKey, 1);
                }
                
            }
                
        }
        
    }
    
    //methods
    
    public void printMap() {
        for (String i : map.keySet()) { //function ripped straight outta w3schools lol
              System.out.println("key: "   i   " value: "   map.get(i));
        }
    } //*/
    
}
  

Текстовый файл:

 ONE TWO ThReE FoUR fIve
six     seven        
EIGHT
     NINE       
TEN ELEVEN
ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE, ONE, ONE,
  

Вывод:

 one,
one,
one,
one
one
one
one
one
one
one
one
one
one
one
one
one
one
eleven
ten
nine
eight
seven
six
five
four
three
two
one

 Hashmap: 

key: nine value: 1
key: one, value: 1
key: six value: 1
key: four value: 1
key: one value: 1
key: seven value: 1
key: eleven value: 1
key: ten value: 1
key: five value: 1
key: three value: 1
key: two value: 1
key: eight value: 1
  

Из того, что я вижу, на выходе должны быть выведены правильные значения для частот клавиш. Заранее спасибо за любую помощь!

Ответ №1:

Вы можете изменить определение FreqCount следующим образом:

 FreqCount(List<String> driverList) {
    for (int dLIndex = driverList.size() - 1; dLIndex >= 0; dLIndex--) {
        String key = driverList.get(dLIndex);
        if (map.get(key) == null) {
            map.put(key, 1);
        } else {
            map.put(key, map.get(key)   1);
        }
    }
}
  

Вывод после этого изменения:

  Hashmap: 

key: nine value: 1
key: one, value: 3
key: six value: 1
key: four value: 1
key: one value: 15
key: seven value: 1
key: eleven value: 1
key: ten value: 1
key: five value: 1
key: three value: 1
key: two value: 1
key: eight value: 1
  

В качестве альтернативы,

 FreqCount(List<String> driverList) {
    for (int dLIndex = driverList.size() - 1; dLIndex >= 0; dLIndex--) {
        String key = driverList.get(dLIndex);
        map.put(key, map.getOrDefault(key, 0)   1);
    }
}
  

Map#getOrDefault возвращает значение, которому сопоставлен указанный ключ, или значение по умолчанию, если эта карта не содержит сопоставления для ключа.

Комментарии:

1. Похоже, это именно то, что мне нужно, но после повторного ввода кода в мой eclipse он по-прежнему печатает все значения как 1. Даже копирование и вставка непосредственно отсюда в код дает те же результаты. Чтобы было ясно, это произошло для всех этих исправлений, а не только для ваших.

2. Неважно! Я предположил, что это проблема eclipse, и перенес все в новый проект. Теперь это работает. Большое спасибо!!

Ответ №2:

Самый простой способ сделать это, imo, — использовать метод Map.merge . Метод принимает предыдущее значение и применяет функцию сопоставления. В этом случае второе значение не используется. Первое значение используется для замены существующего значения 1. Таким образом, вы получаете частоту появления строк.

Также обратите внимание, что я изменил наш класс, чтобы использовать parse метод, который возвращает карту. нецелесообразно выполнять много вычислений в конструкторе класса.

После считывания значений.

          
FreqCount fc = new FreqCount();
Map<String,Integer> map = fc.parse(parsed);
        
map.entrySet().forEach(System.out::println);
  

С принтами

 nine=1
one,=3
six=1
four=1
one=15
seven=1
eleven=1
ten=1
five=1
three=1
two=1
eight=1
  

Модифицированный класс

 
class FreqCount {
    // attributes and initializations
    private Map<String, Integer> map =
            new HashMap<>();
    
    public Map<String, Integer> parse (List<String> driverList) {
        for (String str : driverList) {
            map.merge(str, 1, (v1,notUsed)->v1   1);
        }
        return map;
    }
}
  

Комментарии:

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

Ответ №3:

У вас есть несколько проблем.

 FreqCount(List<String> driverList){
    for(int dLIndex = driverList.size()-1; dLIndex>=0; dLIndex--) { //puts list into hashmap
        if(map.get(driverList.get(dLIndex)) != null) {
            int tval = map.get(driverList.get(dLIndex));
            map.remove(driverList.get(dLIndex));
            map.put(driverList.get(dLIndex), tval 1);
        }else {
            map.put(driverList.get(dLIndex), 1);
        }
    }

}
  

Вы делаете какой-то странный цикл for, пытаясь выполнить итерацию по пустой карте. Вам нужно проверить, есть ли ключ к этому слову на карте, если есть, вы добавляете его к значению, а если нет, вы добавляете новую пару со значением 1.

И чтобы избежать числа с запятыми или точками (если вы хотите, вы можете добавить другие символы в регулярное выражение, которое replaceAll принимает в качестве параметра)

 while(scanFile.hasNext()) { //if this ends up cutting off bottom line, make it a do while loop
        String value = scanFile.next().toLowerCase();
        value = value.replaceAll("[,.]", "");
        parsed.add(value);
    }
  

Теперь вывод

 one
one
one
one
one
one
one
one
one
one
one
one
one
one
one
one
one
eleven
ten
nine
eight
seven
six
five
four
three
two
one

 Hashmap: 

key: nine value: 1
key: six value: 1
key: four value: 1
key: one value: 18
key: seven value: 1
key: eleven value: 1
key: ten value: 1
key: five value: 1
key: three value: 1
key: two value: 1
key: eight value: 1
  

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

Комментарии:

1. Спасибо! Я не думаю, что мы уже рассмотрели регулярное выражение, поэтому я изучу его более подробно, чтобы понять, хочет ли этого мой профессор.