Isogram- a word without repeated letters - java

I want to develop a java code to detect a repeated letter in word and print the desired result but mine keeps iterating and i have no idea on how to get about it. Here is the code:
import java.util.*;
public class Isogram {
public static void main(String[] args){
Scanner input = new Scanner(System.in);
System.out.println("Enter the name: ");
String car = input.nextLine().toLowerCase();
char[] jhd = car.toCharArray();
Arrays.sort(jhd);
for(int ch = 0; ch < jhd.length; ch++){
try {
if (jhd[ch] == jhd[ch + 1]) {// || jhd[ch] == jhd[ch]){
System.out.print("THis is an Isogram");
} else {
System.out.println("Ripu from here");
}
} catch(ArrayIndexOutOfBoundsException ae) {
System.out.println(ae);
}
}
}
}
If u have an adjustment or a better code it will be helpful.

private static String isIsogram(String s){
String[] ary = s.split("");
Set<String> mySet = new HashSet<String>(Arrays.asList(ary));
if(s.length() == mySet.size()){
return "Yes!";
}else{
return "NO";
}
}
Create an array from the string.
Convert array to a List and then create a Set from that List. Set
keeps only unique values.
If set size equals the initial string length then it is an
isogram. If set is smaller than initial string then there were
duplicate characters.

public static boolean isIsogram(String str) {
boolean status = true;
char [] array = str.toCharArray();
Character[] charObjectArray = ArrayUtils.toObject(array);
Map<Character,Integer> map = new HashMap<>();
for (Character i : charObjectArray){
Integer value = 1;
if (map.containsKey(i)){
Integer val = map.get(i);
map.put(i,val+1);
}
else map.put(i,value);
}
for (Integer integer: map.values()){
if (integer>1){
status= false;
break;
}
else status = true;
}
return status;
}
}

Related

how to compare two strings visually in java?

As of now I am making a small program that compares two string with the outcome of true and false. However, the program needs to say true if it visually looks the same. for example if it say box and b0x then it would be true. As of now the outcome is looking false as shown below.
Enter First String:
box
Enter Second String:
b0x
false
the string below needs to be considered the same
0, o and Q
1, I and T
2 and Z
5 and S
8 and B
below is my current work
import java.util.Scanner;
public class Program {
public static void main(String[] args) {
{
Scanner sc = new Scanner(System.in);
System.out.println("Enter First String:");
String str1 = sc.nextLine();
System.out.println("Enter Second String:");
String str2 = sc.nextLine();
sc.close();
String string1 = new String("0");
String string2 = new String("o");
String string3 = new String("q");
String string4 = new String("1");
String string5 = new String("l");
String string6 = new String("T");
String string7 = new String("2");
String string8 = new String("z");
String string9 = new String("5");
String string10 = new String("s");
String string11 = new String("8");
String string12 = new String("b");
// Comparing for String 3 = String 4
if (str1.equals(str2))
{
System.out.print(true);
}
else if(string1.equals(str1))
{
System.out.print(true);
}
else if(string2.equals(str2))
{
System.out.print(true);
}
else
{
System.out.print(false);
}
}
}
}
Is there any algorithm that I can use or any way where the program can detect as true even when they are visually the same. I appreciate any help, thank you
The answer to this question (as well as most questions about pattern matching in Strings) is regular expressions. All you need to do is use replaceAll for all your character transformations to normalize your strings.
like:
str1 = str1.replaceAll("[oQ]", 0);
str1 = str1.replaceAll("[IT]", 1);
First of all, when you declare String variables, you don't have to make a constructor call each time.
String string1 = "0" // that is fine. No need to call constructor.
Then, I advise you to create a collection of all the characters that are supposed to look the same.
Iterate over all the characters of the first input, and check if :
each character of the first input is equal to each character of the second input
if it is a "look visually the same character", check if second input contains the associated(s) character(s).
According to the data you provided, I suggest you this solution, though it is not the perfect one :
import javafx.util.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
private static List<List<Character>> charactersVisuallyLookingTheSame = new ArrayList<>();
public static void main(String[] args) {
initCharactersThatLookTheSame();
Scanner sc = new Scanner(System.in);
System.out.println("Enter First String:");
String str1 = sc.nextLine();
System.out.println("Enter Second String:");
String str2 = sc.nextLine();
sc.close();
boolean equal = equalsVisually(str1, str2);
System.out.println(equal);
}
private static boolean equalsVisually(String first, String second)
{
// some checks just in case...
if(first == null || second == null)
{
return false;
}
// to be equal visually, they must have the same length.
if(first.length() != second.length())
{
return false;
}
char[] firstAsArray = first.toCharArray();
char[] secondAsArray = second.toCharArray();
for(int i = 0; i < firstAsArray.length; i++)
{
// if it is different
if(firstAsArray[i] != secondAsArray[i])
{
if(!isCharVisuallyLookingTheSame(firstAsArray[i], secondAsArray[i]))
{
return false;
}
}
}
return true;
}
private static boolean isCharVisuallyLookingTheSame(char first, char second)
{
// we check if it looks visually the same
for(List<Character> visuallyTheSameList : charactersVisuallyLookingTheSame)
{
boolean doesFirstStringContainVisualChar = false;
for(Character c1 : visuallyTheSameList)
{
if(first == c1)
{
boolean doesSecondStringCharVisuallyEquals = false;
for(Character c2 : visuallyTheSameList)
{
if((second == c2))
{
return true;
}
}
}
}
}
return false;
}
private static void initCharactersThatLookTheSame()
{
// these lists contain all the characters that look visually the same.
// add in here any list of characters that visually look the same.
List<Character> o = new ArrayList<>();
charactersVisuallyLookingTheSame.add(o);
o.add('0');
o.add('o');
o.add('Q');
List<Character> i = new ArrayList<>();
charactersVisuallyLookingTheSame.add(i);
i.add('1');
i.add('I');
i.add('T');
List<Character> z = new ArrayList<>();
charactersVisuallyLookingTheSame.add(z);
z.add('2');
z.add('Z');
List<Character> S = new ArrayList<>();
charactersVisuallyLookingTheSame.add(S);
S.add('5');
S.add('S');
List<Character> B = new ArrayList<>();
charactersVisuallyLookingTheSame.add(B);
B.add('8');
B.add('B');
}
}
Some outputs :
I guess that will do the job. don't hesitate to execute it in debug mode if there are any problems. I fast coded this and I could have made mistakes.
But overall, I suggest you to : use regular expressions. It was suggested by another answer and I think that can only be better than this. Nevertheless, regular expressions can be hard to understand...
The other option would be to build a Map with a common Identifier between similar values. Might be a bit more complicated, but should be more performant than looping a replaceAll multiple times to normalize both values.
import java.util.HashMap;
import java.util.Map;
public class VisuallySimilar
{
public static int id = 0;
public static Map<Character, Integer> map = new HashMap<>();
public static void similarChars(Character... chars) {
for(Character c : chars) {
map.put(c, id);
}
id++;
}
public static boolean areSimilar(String val1, String val2) {
if(val1.length() != val2.length())
return false;
char[] char1 = val1.toCharArray();
char[] char2 = val2.toCharArray();
for(int i = 0; i < char1.length; i++) {
if(char1[i] == char2[i] || map.get(char1[i]) == map.get(char2[i]))
continue;
return false;
}
return true;
}
public static void main(String[] args) {
similarChars('0', 'o', 'O', 'Q');
similarChars('T', 'I', '1');
String val1 = "b0x";
String val2 = "box";
System.out.println("Are Similar: " + areSimilar(val1, val2));
}
}

Faster way to check if we can write message from given letters

I needed to write a function that takes as input two strings. One is the message I want to write and second are given letters. Letters are ordered randomly.There is no guarantee that each letter occurs a similar number of times .some letters might be missing entirely.
The function should determine if I can write message with the given
letters and it should return true or false accordingly.
I coded it and I think it is very fast, but how can I improve it having in mind the string with letters would be very large while the message would be very short?
Is there a fastest way?
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class LetterBowl {
public static void main(String []args){
String message = generateRandomStringUpToThousandChars();
String bowlWithLetters = generateRandomStringUpToThousandChars();
if(canConstructMessage(message, bowlWithLetters)) {
System.out.println("Message '" + message + "' can be constructed with letters from bowl : " + bowlWithLetters);
}
}
public static boolean canConstructMessage(String message, String letters) {
Map<Character,Integer> letterMap = stringToCharacterMap(letters);
char[] messageList = stringToCharacterList(message);
for(char c : messageList) {
if (!containsLetterAndSubtract(c,letterMap))
return false;
}
return true;
}
// checks if map(bowl) contains char andsubtract one char from map(or removes it if it is last one)
public static boolean containsLetterAndSubtract(char c, Map<Character,Integer> letterMap) {
if(letterMap.containsKey(c)) {
if(letterMap.get(c) > 1) {
letterMap.put(c, letterMap.get(c) - 1);
} else {
letterMap.remove(c);
}
return true;
}
return false;
}
public static char[] stringToCharacterList(String message) {
return message.replaceAll(" ", "").toCharArray();
}
public static Map<Character,Integer> stringToCharacterMap(String s) {
Map<Character,Integer> map = new HashMap<Character,Integer>();
for (char c : s.toCharArray()) {
if(map.containsKey(c))
map.put(c, map.get(c) + 1);
else
map.put(c, 1);
}
return map;
}
public static String generateRandomStringUpToThousandChars(){
char[] chars = "abcdefghijklmnopqrstuvwxyz".toCharArray();
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < random.nextInt(1000); i++) {
char c = chars[random.nextInt(chars.length)];
sb.append(c);
}
String output = sb.toString();
return output;
};
}
For large bowl size and smaller msg size i found this would be mor efficient :
public static boolean canConstructMessageSorted(String message, String bowlWithLetters) {
int counter = 0;
boolean hasLetter;
//sorting
char[] chars = bowlWithLetters.toCharArray();
Arrays.sort(chars);
String sortedBowl = new String(chars);
//sorting
chars = message.toCharArray();
Arrays.sort(chars);
String sortedMsg = new String(chars);
for (int i = 0; i < sortedMsg.length(); i++) {
hasLetter = false;
for( ; counter < sortedBowl.length() ; counter++) {
if(sortedMsg.charAt(i) == sortedBowl.charAt(counter)) {
hasLetter = true;
break;
}
}
if(!hasLetter) return false;
}
return true;
}
You're operating at O(message.size + letters.size). This is the lowest worst-case time-complexity that I could figure out, on hand. Referring to the fastest way, there's always more you could do. For example, defining the method
public static char[] stringToCharacterList(String message)
and only using it once is technically time-inefficient. You could have simply put that body of code within the canConstructMessage() method, saving another item from being placed on, and taken off of the stack. Although this is such a small fragment of time, when you say fastest, it could be worth talking about.
For every letter in letters, remove 1 copy of it from the message. If the message ends up empty, the answer is "yes":
public static boolean canConstructMessage(String message, String letters) {
for (int i = 0; i < letters.length(); i++)
message = message.replaceFirst("" + letters.charAt(i), "");
return message.isEmpty();
}
If reusing letters is allowed, you can do it in 1 line:
public static boolean canConstructMessage(String message, String letters) {
return letters.chars().boxed().collect(Collectors.toSet())
.containsAll(message.chars().boxed().collect(Collectors.toSet());
}
I found this would be more efficient for large bowl size and small msg size :
public static boolean canConstructMessageSorted(String message, String bowlWithLetters) {
int counter = 0;
boolean hasLetter;
//sorting
char[] chars = bowlWithLetters.toCharArray();
Arrays.sort(chars);
String sortedBowl = new String(chars);
//sorting
chars = message.toCharArray();
Arrays.sort(chars);
String sortedMsg = new String(chars);
for (int i = 0; i < sortedMsg.length(); i++) {
hasLetter = false;
for( ; counter < sortedBowl.length() ; counter++) {
if(sortedMsg.charAt(i) == sortedBowl.charAt(counter)) {
hasLetter = true;
break;
}
}
if(!hasLetter) return false;
}
return true;
}

Given a Morse String with out any spaces, how to find the no. of words it can represent irrespective of the meaning

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)

Can't figure out what's triggering "java.util.ConcurrentModificationException"

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

Char Arrays comparison

I am trying to build something like a hangman (for beginners)
I try this:
int i = 0;
int fails = 0;
boolean success = false;
boolean retval;
char[] defineword = new char[] { 'h', 'u', 'n', 'g' };
char[] givenchar = new char[0];
char[] testchar = new char[] { 'h' };
while (success == false && fails < 5) {
System.out.println("Give a char: ");
String word = input.next(); // INPUT STRING
givenchar = word.toCharArray(); // CONVERT
retval = Arrays.equals(givenchar, testchar);
System.out.println("THE LETTER IS " + retval);
if (retval == true) {
testchar[0] = defineword[i + 1];
} else {
fails++;
}
}
The problem is that it can't continue after the letter ('u'), it is stuck in 'u'.
One observation I will make is that you only real comparison is between givenchar, and testchar.
retval = Arrays.equals(givenchar, testchar);
It would make sense that this wouldn't work once you got past u because testchar never gets past u either. I think you might have intended to add an i++ somewhere.
testchar[0] = defineword[i + 1];
package mer;
import java.util.Scanner;
public class Aswe
{
int i=0;
int f=0;
boolean suc=false;
boolean ret;
char[] dw=new char[]{'h','u','n','g'};
char[] gc=new char[0];
char[] tc=new char[]{'h'};
Scanner i1;
public void me()
{
i1=new Scanner(System.in);
while(suc==false&&f<5)
{
System.out.println("give a char");
String ch=i1.nextLine();
gc=ch.toCharArray();
ret=eual(gc,tc);
System.out.println("the letter is"+ret);
if(ret)
{
tc[0]=dw[i+1];
}
else
f++;
}
}
public boolean eual(char[] a,char[] b)
{
if(a[0]==b[0])
return true;
else
return false;
}
public static void main(String ... args)
{
new Aswe().me();
}
}
private int fails = 0;
private final int maxFails = 5;
private char[] answer = new char[] {'j','a','v','a'};
public Hangman() {
Scanner scan = new Scanner(System.in);
/*
* Game Loop
*/
while(fails < maxFails){
System.out.print("Enter a char: ");
char givenChar = scan.next().charAt(0);
System.out.println("Given char is: " + Check(answer,givenChar));
}
}
/*
* Check if the char exists in the array.
*/
private boolean Check(char[] array,char value){
for(int i = 0; i < array.length; i++){
if(array[i] == value){
return true;
}
}
/*
* Okay did not find any char that match return false.
*/
return false;
}

Categories