I want to know if there is any way i could optimize this code.
String[] array;
for(String s:array){
if(s.contains("one"))
//call first function
else if(s.contains("two"))
//call second function
...and so on
}
The string is basically lines I am reading from a file.So there can be many number of lines.And I have to look for specific keywords in those lines and call the corresponding function.
This wont stop you code from doing many String#contains calls, however, it will avoid the if/else chaining..
You can create a key-function map and then iterate over the entries of this map to find which method to call.
public void one() {...}
public void two() {...}
private final Map<String, Runnable> lookup = new HashMap<String, Runnable>() {{
put("one", this::one);
put("two", this::two);
}};
You can then iterate over the entry-set:
for(final String s : array) {
for(final Map.Entry<String, Runnable> entry : lookup) {
if (s.contains(entry.getKey())) {
entry.getValue().run();
break;
}
}
}
You can use switch, but in this case i think the if else is the best way
Since you stated that the order of the checks is not important, you can use a combination of regular expression matching and switch:
static final Pattern KEYWORDS=Pattern.compile("one|two|tree|etc");
Matcher m=KEYWORDS.matcher("");
for(String s:array) {
if(m.reset(s).find()) switch(m.group()) {
case "one": //call first function
break;
case "two": //call second function
break;
case "three": //call third function
break;
case "etc": // etc
break;
}
}
Since this will stop at the first match, regardless of which keyword, it is potentially more efficient than checking one keyword after another, for strings containing a match close to the beginning.
Related
I am writing a Processing script and want to detect whether a key (using the key variable) is one of a set of keys chosen by the user ahead of time (say, in a settings JSON file or some other method). I used a switch/case statement to set this up, but it turns out I cannot use variables as case expressions.
I have used the final keyword to mark them as constants, and this works for those defined as direct variables, but not those stored inside an array.
Is there a way of doing this to avoid either using a long if/elseif statement or splitting the array into multiple variables?
In case that's not clear (pun not intended), this is my current code:
// constants
final char[] teamHotkeys = {'b', 'y', 'p', 'o', 'r'};
final char animationHotkey = ' ';
final char doAllHotkey = 'a';
...
// keyPressed handler
void keyPressed(){
float[] chartArea = {gridSizeHeight * 2, gridSizeHeight * 22, gridSizeWidth * 1, gridSizeWidth * 23};
// check which key has been pressed
switch (key){
case teamHotkeys[0]:
drawBar(0, chartArea);
break;
case teamHotkeys[1]:
drawBar(1, chartArea);
break;
case teamHotkeys[2]:
drawBar(2, chartArea);
break;
case teamHotkeys[3]:
drawBar(3, chartArea);
break;
case teamHotkeys[4]:
drawBar(4, chartArea);
break;
case animationHotkey:
runAnimation();
break;
case doAllHotkey:
showFinalScores();
break;
}
}
The compiler can access animationHotkey and doAllHotkey fine, now they are constants, but it can't access the indices of teamHotkeys[] as only the array is a constant, but its members could technically have changed, but the compiler doesnt' know that they never change.
I would just use if statements. Sometimes the dumbest code is the best. It's the easiest to understand, and once it's written you can put it in a function somewhere and not really look at it very much.
But if you really want to avoid if statements, one approach you might take is to create a HashMap that maps character keys to runnable actions. Here's a very basic example:
HashMap<Character, Runnable> keyRunnableMap = new HashMap<Character, Runnable>();
void setup() {
Runnable aRunnable = new Runnable() {
public void run() {
println("A pressed.");
}
};
Runnable bRunnable = new Runnable() {
public void run() {
println("B pressed.");
}
};
keyRunnableMap.put('a', aRunnable);
keyRunnableMap.put('b', bRunnable);
}
void draw() {
}
void keyPressed() {
if (keyRunnableMap.containsKey(key)) {
keyRunnableMap.get(key).run();
}
}
This allows you to keep your keyPressed() logic very short, but it takes more code to set up. You could shorten this a little bit with Java 8 lambdas, but that won't work if you're using the current version of the Processing editor. I don't actually recommend this approach. Just stick with if statements.
Edit: You could also rely on the fact that behind the scenes, the key variable is a char type, which is actually a number. Lower-case 'a' is the number 97. More info here. Here's an example:
Runnable[] runnableArray = new Runnable[2];
void setup() {
Runnable aRunnable = new Runnable() {
public void run() {
println("A pressed.");
}
};
Runnable bRunnable = new Runnable() {
public void run() {
println("B pressed.");
}
};
runnableArray[0] = aRunnable;
runnableArray[1] = bRunnable;
}
void draw() {
}
void keyPressed() {
// a = 97, b = 98
if(key >= 'a' && key <= 'b'){
int index = key - 97;
runnableArray[index].run();
}
}
Again, I don't actually recommend this approach, as it's not any more readable than if statements.
Well, in C# a long switch is basically a HashMap, but in Java it is just... many if/else's, so I usually prefer HashMap when I can use it.
If you only want to know if your key is into a group (let's say, "teamHotkeys") you could use a HashMap<Character, String> or HashMap<Character, KeyType> (with KeyType as an enum), to map multiple keys to a same action. It still will be O(n) in the worst case, but since the keys are grouped it should be better than a lot of if/else's.
HashMap<Character, KeyType> keyTypePairs = new HashMap<Character, KeyType>();
switch(keyTypePairs.get(key)) {
case teamHotkeys:
// ...
break;
}
enum KeyType { teamHotkeys, otherType1, otherType2 }
And that's all, it should work if I did understand the question.
EDIT: With a quick test I can say the space ocuppied by the HashMap is between 2.3 and 3.5 kilobytes in the worst case. About 300 entries = null reserved space, 120 (int)keys and KeyType (each enum uses an 4 or 8 byte address depending on the CPU). Is not THAT much, but I'll take it in acount if I was you. Also, I say 300 as an aproximation; I don't have 120 keys so it could be much less or much more.
I'm trapped within this certain predicament of mine and I would gladly accept any suggestion. So here it is:
I'm currently working in a method where a certain variable is assigned a specific value in the beginning part of the method. During the course of the method, that variable is used as a parameter by an external component which basically returns a result code. In one of those result codes, I have to change the value of the prior mentioned variable and repeat the whole process using the new value. The concept is as follows but I have simplified it as much as possible:
public void myMethod (String args[]) {
String server;
server = "some value";
switch (someExternalOperation(server)) {
case 1:
//process....
break;
case 2:
server = "new value";
//repeat myMethod using new value of server String variable
break;
}
}
public int someExternalOperation (String str) {
//after several operation
return 1; //example purposes
}
By the way, I have checked and researched things like goto and other alternative. I may have overlooked some results and ended up asking here. Any help would be much appreciated. Thank you.
One simple option is to have a private overload taking the server parameter - then you can call it recursively:
public void myMethod(String args[]) {
myMethod(args, "some value");
}
private void myMethod(String[] args, String server) {
switch (someExternalOperation(server)) {
case 1:
// process....
break;
case 2:
myMethod(args, "new value");
break;
}
}
You need to make sure that isn't going to recurse infinitely, of course.
Another option would be to just have a loop inside your method:
public void myMethod (String args[]) {
String server = "some value";
while (true) { // Or ideally a different condition...
switch (someExternalOperation(server)) {
case 1:
// process....
// Done! Exit the method...
return;
case 2:
server = "new value";
// We'll now continue to the next iteration of the loop
break;
default:
// ?
}
}
}
I figured a way instead to address. Thanks everyone for reading. I'll just simply use the external operation and check if it will return case 2.
public void myMethod (String args[]) {
String server;
server = "some value";
if (someExternalOperation(server) == 2)
server = "new value";
switch (someExternalOperation(server)) {
case 1:
//process....
break;
}
}
public int someExternalOperation (String str) {
//after several operation
return 1; //example purposes
}
Thanks for your help anyway.
I wrote a library
This library accept the limited options and print the related string.
public class Lib {
public enum Num {
ZERO,
ONE,
TWO,
THREE
}
public static void main(String[] args) {
Lib obj = new Lib();
obj.print(Num.ONE);
}
public void print(Num num) {
switch (num) {
case ZERO:
System.out.println("ZERO is "+Num.ZERO.ordinal());
break;
case ONE:
System.out.println("ONE is "+Num.ONE.ordinal());
break;
case TWO:
System.out.println("TWO is "+Num.TWO.ordinal());
break;
case THREE:
System.out.println("THREE is "+Num.THREE.ordinal());
break;
default:
break;
}
}
}
In the new version, I will disable option ONE and TWO
public enum Num {
ZERO,
//ONE,
//TWO,
THREE
}
How can I keep the correct values after I disabled the options?
It is not clear what you are asking. If you change any piece of code and thereby "remove" "names" that formerly existed ... than of course, any "reference" to any of the deleted elements is ... first of all: broken.
In case of an enum, you might prefer to not rely on build-in ordinals; instead you could go for this:
enum Whatever {
ONE(1), TWO(2);
private final int value;
private Whatever(value) {
this.value = value;
}
public int getValue() { return value }
But you have to be really careful here. For example, if you are persisting enum objects (into some sort of database for example) then any such change (adding or removing enum "values") will lead to incompatibilities!
I am not sure of what you want to do, but for example you can do this:
public enum Num {
ZERO,
ONE,
TWO,
THREE
}
switch (num) {
case ZERO:
System.out.println("ZERO is "+Num.ZERO.ordinal());
break;
case THREE:
System.out.println("ZERO is "+Num.THREE.ordinal());
break;
case One:
case Two:
default:
break;
You might be disable some of enum now onward and keep stored as it is. To support both the things, you should have a method that returns list of enum that will populate on UI. I.E. List getPopulatedOnUi(). That contains those enum list that you needed.
Don't remove from definition itself. keep as it is. because that will throw error for existing as it might be stored into database.
You can modify the print() method as below: Instead of switch, you can use for loop and make the code little bit generic. Now, even when your enum values changes, you need not to make any changes in this code.
It will handle the case , If you disable some enum values in future.
public void print(Num num) {
for(Num n : Num.values()) {
if(n == num) {
System.out.println(n.name()+ " is " + n.ordinal());
break;
}
}
}
I have an object "ObjectName" defined by String hostName and List serviceList.
two serviceLists might contain one or more of the same string.
Each string is the name of a method. There can't be methods with same name that do different things or methods with different names that do the same things.
Each ObjectName is paired with an unique integer. Then I create a
Map<ObjectName, Integer> objectPorts = new HashMap<>();
I add to this map two nodes with a duplicate string in their serviceList
example:
objectName1's serviceList has {method1, method2, method3}, while objectName2's serviceList has {method4, method2, method5}
objectPorts contains two times the string "method2"
Now I want to search the map for "method2" and execute the code of method2. I want the execution of the code to be done only once, not for each entry of "method2".
how to do this?
I was using this code: (edit: added the switch portion for clarification)
public long executeMethod(String methodName, int n1, int n2) throws Exception {
long result = 0;
for(Map.Entry<Object, Integer> pair : something.entrySet()) {
List<String> methods = pair.getkey().serviceList;
if(methods.contains(methodName) {
switch(methodName) {
case "method1":
result = arithmeticClient.method1(n1, n2);
break;
case "method2":
result = arithmeticClient.method2(n1, n2);
break;
case "method3":
result = arithmeticClient.method3(n1, n2);
break;
case "method4"
result = arithmeticClient.method4(n1, n2);
break;
}
}
return result;
}
but it executes the code of methodName for each instance of methodName present in the map, so it's not what i want it to do. how can I modify it?
From what I am understanding, all you would need to do is break out of the for loop once it has executed. So something like this --
for(Map.Entry<Object, Integer> pair : something.entrySet()) {
List<String> methods = pair.getkey().serviceList;
if(methods.contains(methodName) {
does something
break;
}
}
break is actually a java keyword that "breaks" out of the current loop you are in. So adding it after the code "does something" would mean the loop would exit and the code would only be executed the one time.
The simple way to fix this would be just to add a boolean to indicate if method 2 had been called previously during the loop:
public long executeMethod(String methodName, int n1, int n2) throws Exception {
long result 0;
boolean executed = false;
for(Map.Entry<Object, Integer> pair : something.entrySet()) {
List<String> methods = pair.getkey().serviceList;
if(methods.contains(methodName) && !executed){
//do something
executed = true;
}
}
return result;
}
This will stop method 2 or whatever from being executed more than one time if the value is contained in multiple Lists associated with the objects of your map.
I'm beginner at java, and I'm making a simple program where I type in something, and if what I type in matches one of the things on the "database" then it'll print some text. Is there a simpler way to check this rather than doing this:
int 1;
int 2;
int 3;
etc.
if([USER INPUT].equals("1")) {
System.out.println("TEST");
}
400 times.
Use a switch statement or a HashMap.
Switch statement: Readable, but compiles similarly (if not identically) to an if-else chain.
switch([USER_INPUT]) {
case 1:
System.out.println("TEST");
break;
case 2:
System.out.println("HELLO");
break;
// And so on.
}
Hash Map: Much more readable and simpler. This is preferred.
// Initialization.
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"TEST");
map.put(2,"HELLO");
// Printing.
String s = map.get(USER_INPUT);
if (s == null)
System.out.println("Key doesn't exist.");
System.out.println(s);
Use a HashMap, with key as Integer, and value as text.
System.out.println(myMap.get(USER_INPUT));
Where you have done myMap.put(1, "TEST"); etc, this keeps your code much OO.
the underlying bytecode of switch and if are very comparable, and personally don't see any advantage of switching to switch (unless you want fall through, which means don't include break statement).
A fun alternative would be to use an enum. This would work if you want to define all of the values in a class. It would simplify the code used to get the text value. And it gives you some more fun options beyond what a switch statement would give you.
enum NumberText {
HELLO(1),
WORLD(2);
private static final HashMap<Integer,NumberText> map = new HashMap<Integer,NumberText>();
static{
for (ConnectionGenerator c : ConnectionGenerator.values()) {
map.put(c.code, c);
}
}
Integer code;
NumberText(Integer pCode) {
this.code = pCode;
}
Static ConnectionGenerator getTextFor(Integer code) {
return map.get(code);
}
}
Then to get the text, simply do this:
NumberText nt = NumberText.getTextFor(USER_INPUT);
System.out.println(nt);
You can get fancier and put an additional constructor variable into the enum and have a specific string of text.
enum NumberText {
HELLO(1, "Hello to You"),
GOODBYE(2, "Goodbye");
private static final HashMap<Integer,NumberText> map = new HashMap<Integer,NumberText>();
static{
for (ConnectionGenerator c : ConnectionGenerator.values()) {
map.put(c.code, c);
}
}
Integer code;
String text;
NumberText(Integer pCode, String pText) {
this.code = pCode;
this.text = pText;
}
ConnectionGenerator getNumberTextFor(Integer code) {
return map.get(code);
}
getText() {
return this.text;
}
}
Then you could get the text like this:
NumberText.getNumberTextFor(USER_INPUT).getText();
Use a switch statement.
switch(i){
case 1:
System.out.println("Hi");
break;
case 2:
System.out.println("Ok");
break;
// ...
}
You can use a switch statement.
Here's a quick tutorial and some more in-depth explanation.
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html