Java efficient iterating over char - java

As a part of my implementation I need to implement iterating over chars as efficient as possible. Here is a part of my source code that I wrote:
public int normalize(char s[], int len) {
for (int i = 0; i < len; i++) {
switch (s[i]) {
//numbers
case EN_D0:
case AR_D0:
s[i]= FA_D0;
break;
case EN_D1:
case AR_D1:
s[i]= FA_D1;
break;
case EN_D2:
case AR_D2:
s[i]= FA_D2;
break;
case EN_D3:
case AR_D3:
s[i]= FA_D3;
break;
case EN_D4:
case AR_D4:
s[i]= FA_D4;
break;
case EN_D5:
case AR_D5:
s[i]= FA_D5;
break;
case EN_D6:
case AR_D6:
s[i]= FA_D6;
break;
case EN_D7:
case AR_D7:
s[i]= FA_D7;
break;
case EN_D8:
case AR_D8:
s[i]= FA_D8;
break;
case EN_D9:
case AR_D9:
s[i]= FA_D9;
break;
//Symboles
case EN_QUESTION_MARK:
s[i]=FA_QUESTION_MARK;
break;
case EN_PERCENT_SIGN:
s[i]=FA_PERCENT_SIGN;
break;
case EN_DASH1:
case EN_DASH2:
case EN_DASH3:
case EN_DASH4:
s[i]=FA_DASH;
break;
case HAMZA_ABOVE:
len = delete(s, i, len);
i--;
break;
default:
break;
}
}
return len;
What is the most efficient way of doing such process? Please consider that I did not put all the conditions here because of it was around 600 different conditions. In addition to consider that this part of code should be run for huge documents that have tremendous amount of chars. So the efficiency really matters.

If all the constants in your case statements and assignments are chars, you can use an array to map source char to target char. The length of the array would be 2^16.
char[] map = new char[65536];
...
map[AR_D7] = FA_D7;
...
map[AR_D9] = FA_D9;
...
Then you loop becomes :
for (int i = 0; i < len; i++)
s[i] = map[s[i]];

Related

Converting char to int for in java switch statement [duplicate]

This question already has answers here:
In a switch statement, why are all the cases being executed?
(8 answers)
Closed last year.
I am working on the game where the columns of the board are represented by the characters but i would like to assign them an index.
I have decided to use the switch statement in that case, however it does produce the wrong result.
With the current code I attach, it gives me 14 as an index, however since the string is 7h, and it takes h as a char, it should give an index of 7. What Could be an issue? Thanks in advance!
public class Check {
public int columnToInt(char c) {
int index=0;
switch(c) {
case 'a':
index=0;
case 'b':
index=1;
case 'c':
index=2;
case 'd':
index=3;
case 'e':
index=4;
case 'f':
index=5;
case 'g':
index=6;
case 'h':
index=7;
case 'i':
index=8;
case 'j':
index=9;
case 'k':
index=10;
case 'l':
index=11;
case 'm':
index=12;
case 'n':
index=13;
case 'o':
index=14;
}
return index;
}
public static void main(String[] args) {
String myStr = "7h";
char c =myStr.charAt(1);
System.out.println("the char at position 1 is "+c);
Check check = new Check();
int result = check.columnToInt(c);
System.out.println(result);
}
}
Java switch statements can be a bit annoying to use. You need to use break or all the cases after the expected one will be executed as well.
switch(c) {
case 'a':
index=0;
break;
Alternatively you can use a return.
switch(c) {
case 'a':
return 0;
You must add the break keyword for each case.
For example:
case 'a':
index=0;
break;
otherwise next assignments are applied.

Having trouble creating a bag of objects - Scrabble word search

I have to create a scrabble word search for my data structures class. I haven't reached the actual search yet. First, I need to create a bag of scrabble tiles. However, I keep getting errors when trying to add ScrabbleTile objects to my bag.
I have four classes: ScrabbleTile, ScrabbleBag, ScrabbleHand, and WordFinder.
Here is ScrabbleTile:
public class ScrabbleTile {
private char letter;
private int points;
ScrabbleTile (char letter)
{
this.letter = letter;
switch (letter)
{
case '_':
points = 0;
case 'e':
case 'a':
case 'i':
case 'o':
case 'n':
case 'r':
case 't':
case 'l':
case 's':
case 'u':
points = 1; break;
case 'd':
case 'g':
points = 2; break;
case 'b':
case 'c':
case 'm':
case 'p':
points = 3; break;
case 'f':
case 'h':
case 'v':
case 'w':
case 'y':
points = 4; break;
case 'k':
points = 5; break;
case 'j':
case 'x':
points = 8; break;
case 'q':
case 'z':
points = 10; break;
default: System.out.println("Incorrect character. Please enter a lowercase letter, a-z.");
break;
}
}
public char getLetter()
{
return letter;
}
public int getPoints()
{
return points;
}
}
Here is my ScrabbleBag class:
import DSLib.*;
public class ScrabbleBag {
private BagADT<ScrabbleTile> letterBag;
ScrabbleBag()
{
letterBag = new Bag<>();
for (int i = 0; i < 12; i++) {letterBag.add(ScrabbleTile('e'));}
}
}
In the ScrabbleBag constructor, I'm trying to add the correct number of each letter tile, starting with "e". Netbeans had a few suggestions which I tried, but then it was telling me the line syntax was wrong, after creating a few more instance variables in the ScrabbleBag class. How can I properly add ScrabbleTile objects to the bag?
My professor was very clear that we cannot use methods ahead of what we've gone over in class. Thanks in advance!
I think you missed new when create ScrabbleTitle object
for (int i = 0; i < 12; i++) {
letterBag.add(new ScrabbleTile('e'));
}
To create a ScrabbleTile you need the new keyword:
for (int i = 0; i < 12; i++) {
ScrabbleTile tile = new ScrabbleTile('e');
letterBag.add(tile);
}

How do I assign a number to a letter in Java

I am working on a rather unique coding situation in Java. The purpose of the program I am trying to write is to take an Amateur Radio call sign, convert the letters in the call sign into numbers as defined by a list or other structure, treat the number as its face integer value, and run these numbers through several mathematical operations to output a unique "User Code" at the end. The length of characters, as well as the number itself, will vary from user to user based on their call sign, which is fine. The biggest obstacle I have encountered is that I do not want the letters to be assigned values in a 1-26 or 0-25 type pattern. I will post my code in a moment to show you. For the moment, the end use of this User Code is unimportant to this example, but suffice it to say that since I will be the only one using this particular code example I am not very concerned with doing validity checks or the like as I will ensure the integrity of the input data manually. With this being said, I do have a working solution which I will post here, but my question is not "it doesn't work" because it does work, my problem is that it is, in my opinion, bloated, and something tells me it could be cut down considerably. Here is the code, and following are some alternatives I considered but rejected:
import java.util.*;
import java.io.*;
public class UserCode
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
int baseNumber = 0;
int finalNumber;
String callSign;
System.out.println("Enter CallSign for Code Generation: ");
callSign = in.nextLine();
String s = callSign.toUpperCase();
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if (Character.isDigit(c))
{
int l = Character.getNumericValue(c);
baseNumber = baseNumber + l;
}
else if (Character.isLetter(c))
{
int letNum = 0;
switch (c)
{
case 'A':
letNum = 23;
break;
case 'B':
letNum = 17;
break;
case 'C':
letNum = 5;
break;
case 'D':
letNum = 11;
break;
case 'E':
letNum = 34;
break;
case 'F':
letNum = 18;
break;
case 'G':
letNum = 13;
break;
case 'H':
letNum = 31;
break;
case 'I':
letNum = 27;
break;
case 'J':
letNum = 25;
break;
case 'K':
letNum = 7;
break;
case 'L':
letNum = 25;
break;
case 'M':
letNum = 33;
break;
case 'N':
letNum = 26;
break;
case 'O':
letNum = 28;
break;
case 'P':
letNum = 16;
break;
case 'Q':
letNum = 14;
break;
case 'R':
letNum = 2;
break;
case 'S':
letNum = 4;
break;
case 'T':
letNum = 6;
break;
case 'U':
letNum = 8;
break;
case 'V':
letNum = 10;
break;
case 'W':
letNum = 37;
break;
case 'X':
letNum = 12;
break;
case 'Y':
letNum = 3;
break;
case 'Z':
letNum = 1;
break;
default:
System.out.println("Call Contains a bad character. Try again. \n");
}
baseNumber = baseNumber + letNum;
}
}
System.out.println("\n");
String baseStr = Integer.toString(baseNumber);
System.out.println("The Base number is: " + baseStr + "\n");
int sMod = baseNumber%7;
String sModStr = Integer.toString(sMod);
System.out.println("The Check Digit is: " + sModStr + "\n");
String combine = baseStr + sModStr;
int nextOp = Integer.parseInt(combine);
finalNumber = nextOp * nextOp;
String finalStr = Integer.toString(finalNumber);
System.out.println("The User Code is: " + finalStr + "\n");
}
}
Okay, as I said this code works, but it is long. I had considered a few alternatives, none of which will really work. The first was Enum, but that obviously is outside my parameters as it produces a 1-26 incremented pattern. There are several variations of this using various for{} loops but the result there is the same. I even considered a new HashMap and map.put statements, but that's only marginally shorter and it would seem to me that creating a hashmap would, in the end, actually use more memory than my current solution. As you can see, I did not .split or use .toCharArray() since these created separate entities in memory that I did not need. All the information I needed was already contained in the string itself, except for my chosen numerical values. Finally, I could have created another class file with this assignment code, but the result is the same: the length itself hasn't been changed it's been spread over two files (and actually increased in calling the method).
Having put this out here, can anyone see any way I can shorten this code, particularly in the area of the switch{} block and still retain the same result with the same numerical values? I would be highly interested in any suggestions that can be made in this regard. BTW I didn't mention this but this is not a school assignment, this is a personal project, though my level of Java knowledge is about at that of a beginner taking my first Java class LOL. Thanks
if you want to stick with this logic then use a HashMap<Character, Integer> and setup only once the values like: map.put('Z', 1); and so on, then u don't need the switch. You can get the values by using: map.get('Z'); which will return every time 1
it is easy to implement and easy to change the code if needed
Create an array of corresponding integers:
int[] letnums = {23, 17, 5, ... };
char c = s.charAt(i);
if (Character.isLetter(c)) {
int letnum = letnums(c);
...
}

Switch Statement Inside a method of another class [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
public int alphCheck(char check){
switch(check){
case 'a':
return 1;
break;
case 'b':
return 2;
break;
case 'c':
return 3;
break;
case 'd':
return 4;
break;
case 'e':
return 5;
break;
case 'f':
return 6;
break;
case 'g':
return 7;
break;
case 'h':
return 8;
break;
case 'i':
return 9;
break;
case 'j':
return 10;
break;
case 'k':
return 11;
break;
case 'l':
return 12;
break;
case 'm':
return 13;
break;
case 'n':
return 14;
break;
case 'o':
return 15;
break;
case 'p':
return 16;
break;
case 'q':
return 17;
break;
case 'r':
return 18;
break;
case 's':
return 19;
break;
case 't':
return 20;
break;
case 'u':
return 21;
break;
case 'v':
return 22;
break;
case 'w':
return 23;
break;
case 'x':
return 24;
break;
case 'y':
return 25;
break;
case 'z':
return 26;
break;
}
}
PS.This was done in another class
I want to be able to use this method in the main class, to input a letter, and return a number/index for that letter.
But I kept getting: this method must return a result of type int.
Very Confused. Please help. Thx.
Here's a question to consider: What happens if the inputted letter isn't one of the cases you described?
While you may know that you're only feeding in letters, the compiler doesn't know that, and because it can't figure out what to return if one of the cases you defined isn't hit, emits an error as a result. You'll need to put in a default case, so the compiler knows that the method is guaranteed to return something:
switch(check) {
case 'a':
...
default:
// return something or maybe print/throw an error
}
A better solution for this may be to use the fact that chars are just numbers in a different form. For example, 'a' is equivalent to the integer 97 (check out the table here for a table of characters and their ASCII numerical equivalents). So you can do a math trick to get equivalent results:
public int alphCheck(char check) {
return check - 'a' + 1;
}
you have to provide return type like return 0; at the end of your switch statement.or in default: case
switch(check) {
..
default:
return 0;
}

How to convert an alphanumeric phone number to digits

UPDATE:
The final version of my utility looks like this:
StringBuilder b = new StringBuilder();
for(char c : inLetters.toLowerCase().toCharArray())
{
switch(c)
{
case '0': b.append("0"); break;
case '1': b.append("1"); break;
case '2': case 'a': case 'b': case 'c': b.append("2"); break;
case '3': case 'd': case 'e': case 'f': b.append("3"); break;
case '4': case 'g': case 'h': case 'i': b.append("4"); break;
case '5': case 'j': case 'k': case 'l': b.append("5"); break;
case '6': case 'm': case 'n': case 'o': b.append("6"); break;
case '7': case 'p': case 'q': case 'r': case 's': b.append("7"); break;
case '8': case 't': case 'u': case 'v': b.append("8"); break;
case '9': case 'w': case 'x': case 'y': case 'z': b.append("9"); break;
}
}
return builder.toString();
ORIGINAL QUESTION:
I'm taking on the simple task of converting an alphanumeric phone number to a string of digits. For example, 1-800-HI-HAXOR would become 1-800-44-42967. My initial attempt was to create a nasty switch statement, but I'd love a more elegant, and efficient solution. Here's what I've got:
for(char c : inLetters.toLowerCase().toCharArray())
{
switch(c)
{
case '0': result+="0"; break;
case '1': result+="1"; break;
case '2': case 'a': case 'b': case 'c': result+="2"; break;
case '3': case 'd': case 'e': case 'f': result+="3"; break;
case '4': case 'g': case 'h': case 'i': result+="4"; break;
case '5': case 'j': case 'k': case 'l': result+="5"; break;
case '6': case 'm': case 'n': case 'o': result+="6"; break;
case '7': case 'p': case 'q': case 'r': case 's': result+="7"; break;
case '8': case 't': case 'u': case 'v': result+="8"; break;
case '9': case 'w': case 'x': case 'y': case 'z': result+="9"; break;
}
}
Thanks!
The switch statement is not really that bad. Your algorithm is linear with respect to the length of the phone number. The code is readable and pretty easy to verify by inspection. I wouldn't mess with it, except to add a default case for handling errors. (I'm not a Java programmer, so forgive me if it's called something else.)
If you have to make it faster, a pre-initialized table indexed by character would avoid any comparisons beyond basic error checking. You could even avoid the case conversion by duplicating the values in the table (digit['A'] = digit['a'] = "2";). The cost of initializing the table would be amortized over the total number of conversions.
You could do this using the Apache Commons Lang StringUtils, as follows:
String output = StringUtils.replaceChars(StringUtils.lowerCase(input),
"abcdefghijklmnopqrstuvwxyz",
"22233344455566677778889999");
Assuming speed is not your main concern, of course, and you want a compact solution ;)
Use a Map, where the keys are the letters and digits, and the value is the number on the keypad. (So each keypad number will be indexed by three or four letters and one digit).
Map<Character, Character> keypad = new HashMap<Character, Character>();
...
StringBuilder buf = new StringBuilder(inLetters.length());
for (int idx = 0; idx < inLetters.length(); ++idx) {
Character ch = keypad.get(inLetters.charAt(idx));
if (ch != null)
buf.append(ch);
}
Update: I was curious whether a hand-coded lookup table would perform better than a dense set switch cases. In my casual testing, I found the following code to be the fastest I could come up with:
private static final char[] lut =
"0123456789:;<=>?#22233344455566677778889999[\\]^_`22233344455566677778889999".toCharArray();
private static final char min = lut[0];
String fastest(String letters)
{
int n = letters.length();
char[] buf = new char[n];
while (n-- > 0) {
int ch = letters.charAt(n) - min;
buf[n] = ((ch < 0) || (ch >= lut.length)) ? letters.charAt(n) : lut[ch];
}
return new String(buf);
}
Surprisingly, it was more than twice as fast as similar code using a switch statement (which compiled to a tableswitch instruction). This was just for fun, mind you, but on my laptop, running in a single thread, I could convert 10 million 10-letter-"numbers" in about 1.3 seconds. I was really surprised, because as I understand it, a tableswitch operates in essentially the same way, but I expected it to be faster since it is a JVM instruction.
Of course, unless I were getting paid only for each of a limitless supply of phone numbers I could convert, I would never write code like this. A switch is much more readable, performs well as-is, and is likely to get a free performance boost in some future JVM.
Far and away, the greatest improvement to the original code comes from using a StringBuilder instead of concatenating strings, and that does nothing to impair readability of the code. Using charAt instead of converting the input to a char[] also makes the code simpler and easier to understand and improves performance too. Finally, appending char literals instead of String literals ('1' rather than "1") is a performance improvement that aids readability a little too.
How about simply:
String convert(String inLetters) {
String digits = "22233344455566677778889999";
String alphas = "abcdefghijklmnopqrstuvwxyz";
String result = "";
for (char c : inLetters.toLowerCase().toCharArray()) {
int pos = alphas.indexOf(c);
result += (pos == -1 ? c : digits.charAt(pos));
}
return result;
}
If you want a solution that doesn't force you to enumerate all of the letters, you could do something like:
char convertedChar = c;
if (Character.isLetter(c)) {
//lowercase alphabet ASCII codes: 97 (a)-122 (z)
int charIndex = ((int)c) - 97;
//make adjustments to account for 's' and 'z'
if (charIndex >= 115) { //'s'
charIndex--;
}
if (charIndex == 121) { //'z'-1
charIndex--;
}
convertedChar = (char)(2 + (charIndex/3));
}
result += convertedChar;
If you run this 10^9 times in a tight loop and ctrl-break it a few times, my bet is that nearly every time it will be deep in the string class trying to accomplish one of those innocent-looking "+=" operators.
Switch statements get compiled to a similar form as if-else statements, (each case statement is essentially an if (c == '...') test in disguise) so although this is visually more compact than cascading if's that test for each character, there may or may not be any real performance benefit.
You can potentially streamline it by eliminating some of the comparisons. The key is that char is an integer type (which is why you can switch on a char) so you can use numeric comparison operators. and 'aAssuming your inLetters string only contains alphanumeric characters, this should work... (All other characters will pass through unchanged.)
String result = "";
for (char c : letters.toLowerCase().toCharArray()) {
if (c <= '9') result += c;
else if (c <= 'c') result += "2";
else if (c <= 'f') result += "3";
else if (c <= 'i') result += "4";
else if (c <= 'l') result += "5";
else if (c <= 'o') result += "6";
else if (c <= 's') result += "7";
else if (c <= 'v') result += "8";
else if (c <= 'z') result += "9";
else result += c;
}
The characters of interest have the hexadecimal values: '0' = 0x30, '9' = 0x39, 'a' = 0x61, and 'z' = 0x7a.
Edit: It's better practice to use a StringBuilder and append() to create the string, but for small strings it's not likely to be appreciably faster. (Amdahl's Law demonstrates that the actual speedup you can expect from optimizing code is limited by the percentage of time actually spent in that code.) I only used concatenated strings to make the algorithm clear to the OP.

Categories