I am trying to write a program that has the method public static void method(List<String> words) where the parameter words is a list of words from the text file words.txt that are sorted and contain only the words where each letter occurs only once. For example, the word "feel" would not be included in this list since "e" occurs more than once. The word list is not to be used as an argument in the rest of the program, so the method method is only to be used to store and remember the wordlist for later use. This function can also perform any of the sorting methods.
My thought process was to create a method that would read the text file, and use that text file as the argument in method. method would then filter out all words with letters that appear more than once, and also sort the new list.
When running the program, I'm getting an error "java.util.ConcurrentModificationException: null (in java.util.LinkedList$Listltr)" on the line for (String word : words). Also does the line public static List list; properly save and store the list for later use?
import java.util.*;
import java.io.*;
class ABC
{
public static List<String> list = new LinkedList<String>()
public static List readFile()
{
String content = new String();
File file = new File("words.txt");
LinkedList<String> words = new LinkedList<String>();
try
{
Scanner sc = new Scanner(new FileInputStream(file));
while (sc.hasNextLine())
{
content = sc.nextLine();
words.add(content);
}
}
catch (FileNotFoundException fnf)
{
fnf.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("\nProgram terminated safely");
}
for (String word : words)
{
if (letters(word) == false)
{
list.add(word);
}
}
Collections.sort(list);
return list;
}
public static boolean letters(String word)
{
for (int i = 0; i < word.length() - 1; i++)
{
if (word.contains(String.valueOf(word.charAt(i))) == true)
{
return true;
}
}
return false;
}
public static void main(String args[])
{
System.out.println(readFile());
}
}
The source of the error is that you are changing a list that you are iterating on. This is generally not a good idea.
Since you are building a new list, you don't actually need to change the one you are iterating on. I would recommend changing your code so that the logic for deciding if a letter appears more than once goes in a separate method. This way the complexity of any given method is manageable, and you can test them separately.
So create a new method that tests if any letter appears more than once:
static boolean doesAnyLetterAppearMoreThanOnce(String word) {
...
}
Then you can use it in your existing method:
for (String word : words) {
if (!doesAnyLetterAppearMoreThanOnce(word)) {
list.add(word);
}
}
Collections.sort(list);
Use an iterator. Try it like this.
Iterator<String> it = words.iterator();
while(it.hasNext()) {
CharSequence ch = it.next();
for (int j = 0; j < ch.length(); j++)
{
for (int k = j + 1; k < ch.length(); k++)
{
if (ch.charAt(j) == ch.charAt(k))
{
it.remove(word);
}
}
}
list.add(word);
}
However, I would approach it differently.
String[] data =
{ "hello", "bad", "bye", "computer", "feel", "glee" };
outer: for (String word : data) {
for (int i = 0; i < word.length() - 1; i++) {
if (word.charAt(i) == word.charAt(i + 1)) {
System.out.println("dropping '" + word + "'");
continue outer;
}
}
System.out.println("Keeping '" + word + "'");
List.add(word);
}
Note: You used feel as an example so it wasn't clear if you wanted to check for the same letter anywhere in the word or only adjacent letters that are the same.
There are several problems with you program:
public static List list;
Whenever you see a collection (like List) without a generics - it's a bad smell. Should be public static List<String> list;
Also consider changing public to private.
In readFile() method you mask the class variable 'list' with a local variable 'list'. So your class variable remains uninitialized:
list = new LinkedList<String>();
Better use try-with-resources for scanner:
try(Scanner sc = new Scanner(new FileInputStream(file))) {
You don't need to close it afterwards manually.
You cannot modify the list through which you are iterating. You should either use an iterator and its remove method, or create a new list and append good words to it, instead of removing bad words from the original list.
public static List<String> readFile() {
File file = new File("words.txt");
List<String> list = new ArrayList<>();
try (Scanner scanner = new Scanner(file)) {
while (scanner.hasNextLine()) {
String word = scanner.nextLine();
if (noDuplicates(word)) {
list.add(word);
}
}
Collections.sort(list);
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
return list;
}
private static boolean noDuplicates(String word) {
Set<Character> distinctChars = new HashSet<>();
for (char c : word.toCharArray()) {
if (!distinctChars.add(c)) {
return false;
}
}
return true;
}
I suggest this shorter approach:
public static void method(List<String> words) {
words.removeIf(word -> {
Set<Integer> hs = new HashSet<>();
return word.chars().anyMatch(c -> {
if (hs.contains(c)) return true;
else hs.add(c);
return false;
});
});
System.out.println(words);}
words List now contain only the words where each letter occurs only once.
Given A morse String eg. aet = ".- . -" if the spaces are removed it will become an ambiguous morse string ".-.-" which can represent "aet","eta","ent","etet" etc.
the problem is to find the no.of words that the morse string without spaces can represent irrespective of the meaning of the words. The constraint is that the new word which is formed should be the same size of the input i.e "aet" = "ent" and other words like "etet" should be discarded.
i implemented a recursive solution for some reason it is not working. below is my code and thinking of converting this to DP approach to increase time efficiency. Can some one help to point out the mistake in the below code and is DP a right approach to follow for this problem? Thanks in advance!!
EDIT 1 :- The program gives me an output but not the correct one. for ex. for the morse String representing aet = ".- . -" if given without any spaces to the program ".-.-" it should give an out put "3" i.e 3 words can be formed that is of the same size as the input including the input "aet","eta","ent" but it gives me an output "1". I think there is some thing wrong with the recursive calls.
The approach used here is to simply cut the morse string in a place where first valid morse code is encountered and the repeat the process with the rest of the string untill 3 such valid morse code are found and check whether whole morse string is consumed. if consumed increment the word count and repeat the process for different values of substring size(end variable in the below code).
I hope this helps!!.Tried my best to explain as clearly as I could.
import java.util.*;
import java.io.*;
import java.math.*;
import java.text.*;
public class MorseCode2 {
static Map<String,String> morseCode;
static Map<String,String> morseCode2;
static int count = 0;
public static void main(String args[]){
String[] alpha = {"a","b","c","d","e","f","g","h","i","j","k",
"l","m","n","o","p","q","r","s","t","u","v",
"w","x","y","z"};
String[] morse = {".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",
".--","-..-","-.--","--.."};
morseCode = new HashMap<String,String>();
morseCode2 = new HashMap<String,String>();
for(int i = 0;i<26;i++){
morseCode.put(morse[i],alpha[i]);
}
for(int i = 0;i<26;i++){
morseCode2.put(alpha[i],morse[i]);
}
Scanner in = new Scanner(System.in);
String input = in.next();
String morseString = "";
for(int j = 0; j< input.length(); j++){
morseString += morseCode2.get(input.charAt(j)+"");
}
countPossibleWord(morseString,input.length(),0,1,0);
System.out.println(count);
in.close();
}
public static void countPossibleWord(String s,int inputSize,int start,int end,int tempCount){
if(start >= s.length() || end > s.length()){
return;
}
if(tempCount>inputSize){
return;
}
String sub = s.substring(start, end);
if(sub.length()>4){
return;
}
if(morseCode.get(sub)!=null){
tempCount++;
countPossibleWord(s,inputSize,end,end+1,tempCount);
}
else{
countPossibleWord(s,inputSize,start,end+1,tempCount);
}
if(tempCount == inputSize && end == s.length()){
count++;
}
countPossibleWord(s,inputSize,start,end+1,0);
}
}
EDIT 2 :- Thank you all for your Responses and Extremely sorry for the confusing code, will surely try to improve on writing neat and clear code. learnt a lot from your replies!!
And i also some how made the code work, the problem was I passed wrong argument which changed the state of the recursive calls. Instead of passing "tempCount-1" for the last argument in the last function call in the method "countPossibleWord" i passed "0" this altered the state. found this after running through the code manually for larger inputs. below is the corrected method
public static void countPossibleWord(String s,int inputSize,int start,int end,int tempCount){
if(start >= s.length() || end > s.length()){
return;
}
if(tempCount>inputSize){
return;
}
String sub = s.substring(start, end);
if(sub.length()>4){
return;
}
if(morseCode.get(sub)!=null){
tempCount++;
countPossibleWord(s,inputSize,end,end+1,tempCount);
}
else{
countPossibleWord(s,inputSize,start,end+1,tempCount);
}
if(tempCount == inputSize && end == s.length()){
count++;
}
countPossibleWord(s,inputSize,start,end+1,tempCount-1);
}
}
If you like to have a recursive function, you should be clear about your parameters (use as few as possible) as well as when to step down and when to go up again.
My solution would look something like
public static int countPossibleWord(String strMorse, String strAlpha, int inputSize) {
if (strMorse.length() > 0) { // still input to process
if (strAlpha.length() >= inputSize)
return 0; // String already has wrong size
int count = 0;
for (int i = 0; i < morse.length; i++) { // try all morse codes
if (strMorse.startsWith(morse[i])) { // on the beginning of the given string
count += countPossibleWord(strMorse.substring(morse[i].length()), strAlpha+alpha[i], inputSize);
}
}
return count;
} else {
if( strAlpha.length() == inputSize ) {
System.out.println( strAlpha );
return 1; // one solution has been found
} else {
return 0; // String has wrong size
}
}
}
Your morse and alpha arrays need to be static variables for this to work.
Note that there is only one situation where the recursion will step down: when there is some input left and the size limit is not reached. Then it will check for the next possible letter in the loop.
All other cases will lead the recursion to go one step up again - and when going up, it will return the number of solutions found.
Call it like this:
System.out.println(countPossibleWord(morseString, "", input.length() ));
The fact that you use a class variable instead of the returned value of the recursive function makes it extremely unclear. Even for you as #Thomas Weller said. You should clarify the possible cases when a count one more letter. I deleted eclipse, hence I coded it in C, I hope I will still help you to understand the algo :(understand char* as string)
char morse[26][5] = {".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",
".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."};
int countPossibleWord(char* s, int inputSize, int start, char* buffer, int sizeBuff){
if(start == inputSize){
if(sizeBuff == 0) return 1;
else return 0;
}
char buff[sizeBuff+2]; //
strncpy(buff, buffer, sizeBuff);//
buff[sizeBuff] = s[start]; // buff = buff+s[start]
buff[sizeBuff+1] = '\0'; //
for(int i = 0; i < 26; ++i){
//run the equivalent of your map to find a match
if(strcmp(buff, morse[i]) == 0)
return countPossibleWord(s, inputSize, start+1, "", 0) + countPossibleWord(s, inputSize, start+1, buff, sizeBuff+1);
}
return countPossibleWord(s, inputSize, start+1, buff, sizeBuff+1);
}
The problem with your code is, that you don't understand it any more, because it's not clean as described by Robert C. Martin. Compare your code to the following. This is certainly still not the cleanest, but I think you can understand what it does. Tell me if you don't.
Consider this main program:
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class Program {
public static void main(String[] args) {
String morsetext = enterTextOnConsole();
MorseTable morseTable = new MorseTable();
MorseCode code = convertToMorseCodeWithoutSpaces(morsetext, morseTable);
List<String> guesses = getAllPossibleMeanings(code, morseTable);
List<String> guessesOfSameLength = filterForSameLength(morsetext, guesses);
printListOnConsole(guessesOfSameLength);
}
private static void printListOnConsole(List<String> guessesOfSameLength) {
for (String text : guessesOfSameLength) {
System.out.println(text);
}
}
private static List<String> filterForSameLength(String morsetext, List<String> guesses) {
List<String> guessesOfSameLength = new LinkedList<String>();
for (String guess : guesses) {
if (guess.length() == morsetext.length())
{
guessesOfSameLength.add(guess);
}
}
return guessesOfSameLength;
}
private static List<String> getAllPossibleMeanings(MorseCode code, MorseTable morseTable) {
MorseCodeGuesser guesser = new MorseCodeGuesser(morseTable);
List<String> guesses = guesser.guess(code);
return guesses;
}
private static MorseCode convertToMorseCodeWithoutSpaces(String morsetext, MorseTable morseTable) {
MorseCode code = new MorseCode(morseTable);
code.fromText(morsetext);
code.stripSpaces();
return code;
}
private static String enterTextOnConsole() {
Scanner scanner = new Scanner(System.in);
String text = scanner.next();
scanner.close();
return text;
}
}
and the following MorseTable class:
import java.util.HashMap;
import java.util.Map;
public class MorseTable {
private static final Map<String, String> morseTable;
private static int longestCode = -1;
static
{
morseTable = new HashMap<String, String>();
morseTable.put("a", ".-");
morseTable.put("b", "-...");
morseTable.put("c", "-.-.");
morseTable.put("e", ".");
morseTable.put("t", "-");
morseTable.put("n", "-.");
// TODO: add more codes
for (String code : morseTable.values()) {
longestCode = Math.max(longestCode, code.length());
}
}
public String getMorseCodeForCharacter(char c) throws IllegalArgumentException {
String characterString = ""+c;
if (morseTable.containsKey(characterString)) {
return morseTable.get(characterString);
}
else {
throw new IllegalArgumentException("No morse code for '"+characterString+"'.");
}
}
public int lengthOfLongestMorseCode() {
return longestCode;
}
public String getTextForMorseCode(String morseCode) throws IllegalArgumentException {
for (String key : morseTable.keySet()) {
if (morseTable.get(key).equals(morseCode)) {
return key;
}
}
throw new IllegalArgumentException("No character for morse code '"+morseCode+"'.");
}
}
and the MorseCode class
public class MorseCode {
public MorseCode(MorseTable morseTable)
{
_morseTable = morseTable;
}
final MorseTable _morseTable;
String morseCode = "";
public void fromText(String morsetext) {
for(int i=0; i<morsetext.length(); i++) {
char morseCharacter = morsetext.charAt(i);
morseCode += _morseTable.getMorseCodeForCharacter((morseCharacter));
morseCode += " "; // pause between characters
}
}
public void stripSpaces() {
morseCode = morseCode.replaceAll(" ", "");
}
public MorseCode substring(int begin, int end) {
MorseCode subcode = new MorseCode(_morseTable);
try{
subcode.morseCode = morseCode.substring(begin, end);
} catch(StringIndexOutOfBoundsException s) {
subcode.morseCode = "";
}
return subcode;
}
public MorseCode substring(int begin) {
return substring(begin, morseCode.length());
}
public String asPrintableString() {
return morseCode;
}
public boolean isEmpty() {
return morseCode.isEmpty();
}
}
and last not least, the MorseCodeGuesser
import java.util.LinkedList;
import java.util.List;
public class MorseCodeGuesser {
private final MorseTable _morseTable;
public MorseCodeGuesser(MorseTable morseTable) {
_morseTable = morseTable;
}
public List<String> guess(MorseCode code) {
List<String> wordList = new LinkedList<String>();
if (code.isEmpty()) return wordList;
for(int firstCodeLength=1; firstCodeLength<=_morseTable.lengthOfLongestMorseCode(); firstCodeLength++) {
List<String> guesses = guess(code, firstCodeLength);
wordList.addAll(guesses);
}
return wordList;
}
private List<String> guess(MorseCode code, int firstCodeLength) {
MorseCode firstCode = code.substring(0, firstCodeLength);
String firstCharacter;
try{
firstCharacter = _morseTable.getTextForMorseCode(firstCode.asPrintableString());
} catch(IllegalArgumentException i) {
return new LinkedList<String>(); // no results for invalid code
}
MorseCode remainingCode = code.substring(firstCodeLength);
if (remainingCode.isEmpty()) {
List<String> result = new LinkedList<String>();
result.add(firstCharacter); // sole result if nothing is left
return result;
}
List<String> result = new LinkedList<String>();
List<String> remainingPossibilities = guess(remainingCode);
for (String possibility : remainingPossibilities) {
result.add(firstCharacter + possibility); // combined results
}
return result;
}
}
I have pasted my own solution to it. I have followed DFS and it is giving the correct answer for the given problem statement. Please ask if there are any queries.
alpha =["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
key = [".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--",
"-..-","-.--","--.."]
dic = dict(list(zip(key,alpha)))
def morse_code(morse,count,res,char,length):
global dic
if count == length - 1:
if morse[char:] in dic:
res = res + 1
return res
word = ''
for i in range(char,len(morse)):
word = word + morse[i]
if word not in dic:
continue
else:
count = count + 1
res = morse_code(morse,count,res,i+1,length)
count = count - 1
return res
if __name__ = 'main'
inp = input()
morse = ''
for i in inp:
morse = morse + key[ord(i)-ord('a')]
result = morse_code(morse,0,0,0,len(inp))
print(result)
My code is throwing an error that I've never seen before. So hey! I guess I'm learning ;) Anyway, I did some reading and generally this error is thrown when a list that is being iterated over is modified mid-iteration. However, I'm pretty sure I'm not modifying it. While the error is being thrown on partition(), if I don't assign a new value for currentList in updateCurrentList() (by commenting out the code), the program no longer throws the error. These two functions are called one after the other in my play() method, however the list iteration should be complete by the time the change is made. What am I missing? Do I have to close down the iterator somehow?
package hangman;
import java.io.*;
import java.util.*;
import javax.swing.JOptionPane;
public class Hangman {
private Map<String, List<String>> wordPartitions; // groups words according to positions of guessed letter
private List<String> currentList; // remaining possible words that fit the information given so far
Set<Character> wrongGuesses; // holds all the "wrong" guesses so far
StringBuilder guessString; // current state of the word being guessed
String justHyphens; // for checking whether a guess was "wrong"
// initialize fields
// currentList should contain all (and only) words of length wordLength
// justHyphens and guessString should consist of wordLength hyphens
public Hangman(int wordLength) throws FileNotFoundException {
this.currentList = new ArrayList<String>();
addWords(wordLength);
wrongGuesses = new HashSet();
for(int i = 0; i < wordLength; i++) {
justHyphens += "-";
}
guessString = new StringBuilder();
wordPartitions = new HashMap();
}
private void addWords(int wordLength) throws FileNotFoundException {
Scanner words = new Scanner(new File("lexicon.txt"));
String word = "";
while(words.hasNext()) {
word = words.next();
if (word.length() == wordLength) {
currentList.add(word);
}
}
}
// main loop
public void play() {
char choice;
do {
choice = getUserChoice();
partition(choice);
updateCurrentList(choice);
} while (!gameOver());
endMessage();
}
// display the guessString and the missed guesses
// and get the next guess
private char getUserChoice() {
//generate a string from the incorrect choices char list
String wrong = "";
char letter;
if(!wrongGuesses.isEmpty()) {
Iterator<Character> letters = wrongGuesses.iterator();
letter = letters.next();
while(letters.hasNext()) {
letter = letters.next();
wrong += ", " + letter;
}
}
String letterStr = JOptionPane.showInputDialog("Incorrect choices: "+ wrong +"\n Tested letters: "+ guessString.toString() +"\nplease input a letter.");
return letterStr.charAt(0);
}
// use wordPartitions to partition currentList using
// keys returned by getPartitionKey()
private void partition(char choice) {
String word = "";
String key = "";
List<String> tempList = new ArrayList<String>();
Iterator<String> words = currentList.iterator();
//Generate a key for each word and add to appropriate arraylist within map.
while(words.hasNext()) {
word = words.next();
key = getPartitionKey(word, choice);
if(wordPartitions.containsKey(key)) {
tempList = wordPartitions.get(key);
tempList.add(word);
wordPartitions.put(key, tempList);
} else {
tempList.clear();
tempList.add(word);
wordPartitions.put(key, new ArrayList<String>());
}
}
}
// update currentList to be a copy of the longest partition
// if choice was "wrong", add choice to wrongGuesses
// if choice was "right", update guessString
private void updateCurrentList(char choice) {
String key = findLongestList();
currentList = wordPartitions.get(key);
if(key.equals(justHyphens)) {
wrongGuesses.add(choice);
} else {
addLetterToGuessString(guessString, choice, key);
}
}
private String findLongestList() {
Set<String> keySet = wordPartitions.keySet();
Iterator<String> keys = keySet.iterator();
String maxKey = "";
int maxKeyLength = 0;
List<String> tempList;
String tempKey = "";
while(keys.hasNext()) {
tempKey = keys.next();
tempList = wordPartitions.get(tempKey);
if(tempList.size() > maxKeyLength) {
maxKeyLength = tempList.size();
maxKey = tempKey;
}
}
return maxKey;
}
// checks for end of game
private boolean gameOver() {
return false;
}
// display the guessString and the missed guesses
// and print "Congratulations!"
private void endMessage() {
JOptionPane.showMessageDialog(null, "Congrats, yo!");
}
// returns string with '-' in place of each
// letter that is NOT the guessed letter
private String getPartitionKey(String s, char c) {
String word = "";
String letter = Character.toString(c);
for(int i = 0; i < s.length(); i++) {
if(s.charAt(i) == c) {
word += letter;
} else {
word += "-";
}
}
return word;
}
// update guessString with the guessed letter
private void addLetterToGuessString(StringBuilder guessString, char letter, String key) {
for(int i = 0; i < key.length(); i++) {
if(key.charAt(i) != '-') {
guessString.setCharAt(i, key.charAt(i));
}
}
}
}
The problem is that you are modifying a collection while you are iterating over it.
The collection is currentList, you are iterating over it in partition(). You modify it when you add a word to tempList here:
key = getPartitionKey(word, choice);
if(wordPartitions.containsKey(key)) {
tempList = wordPartitions.get(key);
tempList.add(word);
wordPartitions.put(key, tempList);
} else {
Why ? Because previously you called updateCurrentList() from play():
do {
choice = getUserChoice();
partition(choice);
updateCurrentList(choice);
} while (!gameOver());
And you updated currentList:
String key = findLongestList();
currentList = wordPartitions.get(key);
So, if the key returned by getPartitionKey(word, choice) is the same as the key previously returned by findLongestList(), currentListwill be the same as tempList, and so you will be modifying the collection you are iterating over.
The solution ? If tempList is the same as currentList, don't add the word to it (it already have the word, by definition). So, you can rewrite your if-else like that (I removed some useless code):
if(wordPartitions.containsKey(key)) {
tempList = wordPartitions.get(key);
} else {
wordPartitions.put(key, new ArrayList<String>());
}
if (tempList!=currentList) {
tempList.add(word);
}
I'm working on a project which has a dictionary of words and I'm extracting them and adding them to an ArrayList as word objects. I have a class called Word as below.
What I'm wondering is how do I access these word objects to update the frequency? As part of this project, I need to only have one unique word, and increase the frequency of that word by the number of occurrences in the dictionary.
Word(String word)
{
this.word = word;
this.freq = 0;
}
public String getWord() {
return word;
}
public int getFreq() {
return freq;
}
public void setFreq() {
freq = freq + 1;
}
This is how I am adding the word objects to the ArrayList...I think it's ok?
String pattern = "[^a-zA-Z\\s]";
String strippedString = line.replaceAll(pattern, "");
line = strippedString.toLowerCase();
StringTokenizer st = new StringTokenizer(line);
while (st.hasMoreTokens())
{
String newWord = st.nextToken();
word.add(new Word(newWord));
count++;
}
Instead of an ArrayList use a Bag. This keeps the counts for you.
Use a map to store the words and the Word Object. Ideally a hashset is enough to do this. But internally a hashset is going to use a HashMap anyway. The following piece of code will also be useful for you to increase the frequency of the words that you had already inserted.
Map<String, Word> wordsMap = new HashMap<String, Word>();
String pattern = "[^a-zA-Z\\s]";
String strippedString = line.replaceAll(pattern, "");
line = strippedString.toLowerCase();
StringTokenizer st = new StringTokenizer(line);
while (st.hasMoreTokens())
{
String newWord = st.nextToken();
if(!wordsMap.containsKey(newWord)){
wordsMap.put(newWord, new Word(newWord));
}else{
Word existingWord = wordsMap.get(newWord);
existingWord.setFreq();
}
count++;
}
I would solve the problem with the following code:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Word {
private final String word;
private int frequency;
public Word(String word) {
this.word = word;
this.frequency = 0;
}
public String getWord() {
return word;
}
public int getFrequency() {
return frequency;
}
public void increaseFrequency() {
frequency++;
}
I didn't call this method setFrequency because it is not a real setter method. For a real setter method, you would pass it exactly one parameter.
public static List<Word> histogram(String sentence) {
First, compute the frequency of the individual words.
String[] words = sentence.split("\\W+");
Map<String, Word> histo = new HashMap<String, Word>();
for (String word : words) {
Word w = histo.get(word);
if (w == null) {
w = new Word(word);
histo.put(word, w);
}
w.increaseFrequency();
}
Then, sort the words such that words with higher frequency appear first.
If the frequency is the same, the words are sorted almost alphabetically.
List<Word> ordered = new ArrayList<Word>(histo.values());
Collections.sort(ordered, new Comparator<Word>() {
public int compare(Word a, Word b) {
int fa = a.getFrequency();
int fb = b.getFrequency();
if (fa < fb)
return 1;
if (fa > fb)
return -1;
return a.getWord().compareTo(b.getWord());
}
});
return ordered;
}
Finally, test the code with a simple example.
public static void main(String[] args) {
List<Word> freq = histogram("a brown cat eats a white cat.");
for (Word word : freq) {
System.out.printf("%4d %s\n", word.getFrequency(), word.getWord());
}
}
}
You can use a google collections' Multiset of String instead of the Word class