I am trying to do something like this, i.e., use an array in a switch statement. Is it possible in Java? If it isn't, please explain a possible solution.
boolean[] values = new boolean[4];
values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;
switch (values) {
case [true, false, true, false]:
break;
case [false, false, true, false]:
break;
default:
break;
}
#sᴜʀᴇsʜ ᴀᴛᴛᴀ is right. But I wanted to add something. Since Java 7, switch statements support Strings, so you could do something with that. It is really dirty and I do not recommend, but this works:
boolean[] values = new boolean[4];
values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;
switch (Arrays.toString(values)) {
case "[true, false, true, false]":
break;
case "[false, false, true, false]":
break;
default:
break;
}
For those concerned about performance: you are right, this is not super fast. This will be compiled into something like this:
String temp = Arrays.toString(values)
int hash = temp.hashCode();
switch (hash)
{
case 0x23fe8da: // Assume this is the hashCode for that
// original string, computed at compile-time
if (temp.equals("[true, false, true, false]"))
{
}
break;
case 0x281ddaa:
if (temp.equals("[false, false, true, false]"))
{
}
break;
default: break;
}
NO, simply you cannot.
SwitchStatement:
switch ( Expression ) SwitchBlock
The type of the Expression must be char, byte, short, int, Character, Byte, Short, Integer, String, or an enum type (§8.9), or a compile-time error occurs.
http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.11
You can't switch on whole arrays. But you could convert to a bit set at the expense of some readability of the switch itself:
switch (values[0] + 2 * values[1] + 4 * values[2] + 8 * values[3])
and use binary literals in your case statements: case 0b0101 is your first one.
Try this solution:
boolean[] values = new boolean[4];
values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;
if (ArrayUtils.isEquals(values, new boolean[] {true, false, true, false})) {
...
}
else if (ArrayUtils.isEquals(values, new boolean[] {false, false, true, false})) {
...
}
else {
...
}
See docs here.
Yes, you can pass an array to a switch. The catch is that I'm not talking about Java arrays, but a data structure.
An array is a systematic arrangement of objects, usually in rows and columns.
What you are trying to do is implement a system that recognizes different flags and depending on the flags that are turned on or off you take different actions.
Example
A popular implementation of such mechanism is Linux file permissions. Where you have rwx as the "array of flags".
If the whole array is true, you'll see rwx, which means that you have all the permissions. If you are not allowed to perform any action on a file, the whole array is false, you'll see ---.
Implementation
Guess what, you can think of integers as arrays. An integer is represented by an "array of bits".
001 // 1, if on, set x
010 // 2, if on, set w
100 // 4, if on, set r
// putting it all together in a single "array" (integer)
111 // 2^2 + 2^1 + 2^0 = 4 + 2 + 1 = 7
That is why the permission rwx can be represented as a 7
Java snippet:
class Flags {
public static void main(String args[]) {
/**
* Note the notation "0b", for binary; I'm using it for emphasis.
* You could just do:
* byte flags = 6;
*/
byte flags = 0b110; // 6
switch(flags) {
case 0: /* do nothing */ break;
case 3: /* execute and write */ break;
case 6: System.out.println("read and write\n"); break;
case 7: /* grant all permissions */ break;
default:
System.out.println("invalid flag\n");
}
}
}
To know more about using a binary format, check this question: In Java, can I define an integer constant in binary format?
Performance
Saves memory
You don't have to do extra processing, switches or any other type of juggling.
C programs that require to be as efficient as possible use this type of mechanism; they use flags represented with single bits.
No, you cannot, however you can replace the above with the following (dirty I admit) code:
boolean[] values = new boolean[4];
values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;
switch(makeSuitableForSwitch(values)) {
case 1010:
break;
case 10:
break;
default:
break;
}
private int makeSuitableForSwitch( boolean[] values) {
return (values[0]?1:0)*1000+(values[1]?1:0)*100+(values[2]?1:0)*10+(values[3]?1:0);
}
If you're trying to determine if a set of conditions is true, I'd use bitwise fields instead.
For example,
public class HelloWorld
{
// These are the options that can be set.
// They're final so treated as constants.
static final int A=1<<0, B=1<<1, C=1<<2, D=1<<3 ;
public static void main(String []args)
{
// Now I set my options to have A=true, B=true, C=true, D=false, effectively
int options = A | B | C ;
switch( options )
{
case (A):
System.out.println( "just A" ) ;
break ;
case (A|B):
System.out.println( "A|B" ) ;
break ;
case (A|B|C): // Final int is what makes this work
System.out.println( "A|B|C" ) ;
break ;
default:
System.out.println( "unhandled case" ) ;
break ;
}
}
}
I'd compute a value based on the sequence of the elements in the boolean array, i.e. [true, false, true, true] would evaluate to 1011 and then based on this integer value you can use switch statement.
As of JRE 1.7, you will need to use a hack, I recommend:
Assume values.length <= 64
Convert values to a long representing bitflags
Switch against hexadecimal magic numbers
Java Code Hack:
if(values.length > 64)
throw new IllegalStateException();
long bitflags = 0x0L;
for(int i=0; i< values.length; ++i)
if(values[i])
bitflags |= 0x01L << i;
switch(bitflags) {
case 0xEL: // represents [true, true, true, false]
break;
case 0xAL: // represents [true, false, true, false]
break;
case 0x2L: // represents [false, false, true, false]
break;
default:
break;
}
This answer is not Java, but Haxe because it is possible in it, thanks to pattern matching and has interesting output, which might be useful for you to find a switch that does what you are asking for. Arrays can be matched on fixed length.
I created an demo that compiles to Javascript and Flash. You can see the js-output in the right column.
Demo:
http://try.haxe.org/#86314
class Test {
static function main(){
var array=[true,false,true];
var result=switch(array){
case [true,true,false]: "no";
case [true,false,true]: "yes";
default:"??";
}
#if js
new js.JQuery("body").html(result);
#elseif flash
trace(result);
#end
// ouputs: "yes"
}
}
This is the outputted switch, it uses nested switches. If you play with the cases, you see how the js-ouput changes to have a efficient switch.
(function () { "use strict";
var Test = function() { };
Test.main = function() {
var array = [true,false,true,false];
var result;
switch(array.length) {
case 4:
switch(array[0]) {
case true:
switch(array[1]) {
case false:
switch(array[2]) {
case true:
switch(array[3]) {
case false:
result = "no";
break;
default:
result = "??";
}
break;
default:
result = "??";
}
break;
default:
result = "??";
}
break;
case false:
switch(array[1]) {
case false:
switch(array[2]) {
case true:
switch(array[3]) {
case false:
result = "yes";
break;
default:
result = "??";
}
break;
default:
result = "??";
}
break;
default:
result = "??";
}
break;
}
break;
default:
result = "??";
}
new js.JQuery("body").html(result);
};
var js = {};
var q = window.jQuery;
js.JQuery = q;
Test.main();
})();
Another interesting pattern that you can use underscores. a _ pattern matches anything, so case _: is equal to default, which makes you able to do this:
var myArray = [1, 6];
var match = switch(myArray) {
case [2, _]: "0";
case [_, 6]: "1";
case []: "2";
case [_, _, _]: "3";
case _: "4";
}
trace(match); // 1
http://haxe.org/manual/pattern_matching#array-matching
The answer is NO. The best explain is learn how to use the switch statement.
Here is another approach requiring no imports nor libraries:
boolean[] values = new boolean[4];
values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;
int mask = buildMask(values);
if (areEquals(mask, true, false, true, false)) {
// ...
} else if (areEquals(mask, false, false, true, false)) {
// ...
} else {
// ...
}
private int buildMask(boolean... values) {
int n = 0;
for (boolean b : values) {
n = (n << 1) | (b ? 1 : 0);
}
return n;
}
private boolean areEquals(int mask, boolean... values) {
return mask == buildMask(values);
}
You can also take a look at how Groovy implements the isCase() methods in Java, use a simpler version that fits your needs. It's possible to put that in an interface and create a DSL to compare any two object in your application.
return isCase(DefaultTypeTransformation.asCollection(caseValue), switchValue);
The relevant code is covered in Lines 877 through Lines 982
#Todor Yes, THIS IS POSSIBLE IN JAVA.
boolean[] values = new boolean[4];
values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;
String values_asString = Arrays.toString(values);
switch (values_asString) {
case "[true, false, true, false]":
break;
case "[false, false, true, false]":
break;
case "[true, false, false, true]":
System.out.println("YAAAAAAAAAA GOT IT");
break;
default:
break;
}
Here I just converted an array to a string format and then matched it in the switch case.
I would use constant int values that represent the boolean state.
If you use Java 1.7 or above you can use binary literals that are more readable.
public static final int TRUE_FALSE_TRUE_FALSE = 0b1010;
public static final int FALSE_FALSE_TRUE_FALSE = 0b0010;
for Java 1.6 and below use any other int literals, e.g. hex
public static final int TRUE_FALSE_TRUE_FALSE = 0xA;
public static final int FALSE_FALSE_TRUE_FALSE = 0x2;
then create a method that converts a boolean array to an integer bitset. E.g.
public static int toIntBitSet(boolean...values){
int bitset = 0;
for (boolean value : values) {
bitset = (bitset << 1) | (value ? 1 : 0);
}
return bitset;
}
Finally use the constants in your switch statement
boolean[] values = new boolean[]{true, false, true, false};
int bitset = toIntBitSet(values);
switch (bitset) {
case TRUE_FALSE_TRUE_FALSE:
System.out.println(Integer.toBinaryString(bitset));
break;
case FALSE_FALSE_TRUE_FALSE:
System.out.println(Integer.toBinaryString(bitset));
break;
default:
break;
}
Another approach might be to use a java BitSet and a Map that maps to the logic that should be executed depending on the bitset's value.
public static void main(String[] args) throws Exception {
Map<BitSet, Callable<String>> bitSetMap = new HashMap<>();
bitSetMap.put(bitSetValueOf(true, false, true, false), new TrueFalseTrueFalseCallable());
bitSetMap.put(bitSetValueOf(false, false, true, false), new FalseFalseTrueFalseCallable());
boolean[] values = new boolean[]{true, false, true, false};
BitSet bitset = bitSetValueOf(values);
Callable<String> callable = bitSetMap.get(bitset);
if (callable == null) {
callable = new DefaultCallable();
}
String result = callable.call();
System.out.println(result);
}
public static BitSet bitSetValueOf(boolean... values) {
BitSet bitSet = new BitSet();
for (int i = 0; i < values.length; i++) {
bitSet.set(i, values[i]);
}
return bitSet;
}
and implement your logic
class FalseFalseTrueFalseCallable implements Callable<String> {
#Override
public String call() throws Exception {
return "0010";
}
}
class TrueFalseTrueFalseCallable implements Callable<String> {
#Override
public String call() throws Exception {
return "1010";
}
}
class DefaultCallable implements Callable<String> {
#Override
public String call() throws Exception {
return "default value";
}
}
Related
In teaching myself Android programming (using Android Studio) I am working on a basic calculator app. My eval method uses Dijkstra's shunting-yard algorithm to parse a string expression and calculate the result. I got the idea for this from this SO question.
The code for my Evaluator class is as follows:
class Evaluator {
private static Evaluator instance = new Evaluator();
private Stack< String > mOperators;
private Stack< Double > mOperands;
public static Evaluator getInstance() {
return instance;
}
private Evaluator() {
mOperands = new Stack< Double >();
mOperators = new Stack< String >();
}
public Double eval( String expression ) {
Stack stack = convertExpressionToStack( expression );
buildOperationStacks( stack );
return doEval();
}
private Double doEval() {
while ( !mOperators.isEmpty() ) {
String op = mOperators.pop();
Double v = mOperands.pop();
switch ( op ) {
case "+":
v = mOperands.pop() + v;
break;
case "-":
v = mOperands.pop() - v;
break;
case "*":
v = mOperands.pop() * v;
break;
case "/":
v = mOperands.pop() / v;
break;
}
mOperands.push( v );
}
return mOperands.pop();
}
private void buildOperationStacks( Stack stack ) {
while ( !stack.isEmpty() ) {
String s = ( String ) stack.pop();
switch ( s ) {
case "+":
case "-":
case "*":
case "x":
case "X":
case "/":
case "÷":
if ( s.equals( "x" ) || s.equals( "X" ) ) {
s = "*";
} else if ( s.equals( "÷" ) ) {
s = "/";
}
mOperators.push( s );
break;
default:
try {
if ( !stack.isEmpty() && stack.peek().equals ( "." ) ) {
s += stack.pop();
s += stack.pop();
}
mOperands.push( Double.parseDouble( s ) );
} catch ( Exception e ) {
Log.e( "Error", e.getMessage() );
}
}
}
}
private Stack convertExpressionToStack( String expression ) {
Stack< String > s = new Stack< String >();
for ( char c : expression.toCharArray() ) {
s.push( String.valueOf( c ) );
}
return s;
}
}
So my issue is in the doEval method. When I pop the elements off each stack I am getting the first elements added to each stack. I was of the impression that stacks were a First In Last Out structure.
So what might I be doing wrong? Do I need to somehow reverse each stack?
Thank you.
EDIT
So for example, I input 5+3*2. I would expect the execution to be
pass 1: value1 = 2, Operator1 = *, value2 = 3 result = 6
pass 2: Value1 = 6 (result of pass 1) Operator1 = +, value2 = 5 result = 11
However, when I debug this, I am seeing:
pass 1: value1 = 5, Operator1 = +, value2 = 3, result = 8
pass 2: value1 = 8 (result of pass 1), operator1 = *, value2 = 2, result = 16
Your convertExpressionToStack() method is building an operand stack in the correct order, and then your buildOperstionStack() method is inverting it, by popping one and pushing the other.
You don't really need this second method anyway: just change the evaluation method to understand x as multiplication, etc.
Your understanding of Stack is correct. It's First In, Last Our or Last In, First Out.
About your code:
while ( !mOperators.isEmpty() ) {
String op = mOperators.pop();
Double v = mOperands.pop();
switch ( op ) {
case "+":
v = mOperands.pop() + v;
break;
case "-":
v = mOperands.pop() - v;
break;
case "*":
v = mOperands.pop() * v;
break;
case "/":
v = mOperands.pop() / v;
break;
}
mOperands.push( v );
}
Since, you have not mentioned, how exactly is it behaving, so, let me explain. Suppose, mOperators has +,- where + was added first and - on top of +
mOperands has 3,6,2,1
This is how your code will and should behave.
For the first iteration of while loop:
op = -
v = 1
v = 2-1 = 1 // since it's a -
mOperands is now: 3,6
and after call, mOperands.push(v), since v now is 1, your mOperands will be:
3,6,1
Second iteration of while loop:
op = +
v = 1
v = 6+1 = 7 // since op is +
Your mOperands now looks like: 3
And once it breaks from switch, it will make mOperands as:
3,7
P.S: You should debug your application to see values of mOperands at each and every step to get better idea, why it's behaving like it is.
In short, the user will input a number (say 1 through 3). This will decide which range of numbers the loop should search through.
switch(input){
case 1:
searchTerm = "i<10 && i>5";
case 2:
searchTerm = "i>=10 && i<19";
case 3:
searchTerm = "i>19 && i<24";
}
while(searchTerm){
//some function
}
Is this possible? I I've not been able to find a way to use a string as search parameters.
EDIT: I don't think I did a very good job of explaining why I needed this. What is one to do if there are different numbers of parameters? For example:
case 1:
searchTerm = "i<5"
case 2:
searchTerm = "i>25 && i<29"
case 3:
searchTerm = "(i<50 && i>25) && (i>55 && i<75)"
case 4:
searchTerm = "(i<20 && i>15) && (i>300 && i<325) && (i>360 && i<380)
Then how does one do it? Multiple loops that call the same function?
The correct way to do this is to not use a string at all:
int min, max;
switch(input){
case 1: // i<10 && i>5
min = 6;
max = 10;
break; // to avoid follow-through to the next case
case 2: // i>=10 && i<19
min = 10;
max = 20;
break;
case 3: // i>19 && i<24
min = 20;
max = 25;
break;
default:
// You need something here in case the value entered wasn't 1-3
}
for (int i = min; i < max; ++i) {
// ...
}
Re your edit:
I don't think I did a very good job of explaining why I needed this. What is one to do if there are different numbers of parameters?
In that case, you'll have to use an expression evaluator (or write one, which is a non-trivial task). There's one in Spring, for instance (not recommending, just happened to hear about it). A search for "Java expression evaluator" should turn up some options.
Another alternative, which is somewhat amusing given that some folks mistook your question for a JavaScript question, is to use the JavaScript evaluator built into Java (either Rhino or Nashorn). E.g.: Live Example
import javax.script.*;
class Ideone {
public static void main(String[] args) throws java.lang.Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
String searchTerm = "i >= 19 && i <= 24";
int i;
try {
i = 19;
engine.put("i", i);
while ((boolean)engine.eval(searchTerm)) {
System.out.println("i = " + i);
++i;
engine.put("i", i);
}
System.out.println("Done");
} catch (ScriptException scriptException) {
System.out.println("Failed with script error");
}
}
}
...but you'll still have the problem of determining what initial value to use for i, which I've hardcoded above.
In Java 8 you can select a lambda instead of String:
Predicate<Integer> searchTerm = (Integer v) -> false;
switch (input) {
case 1:
searchTerm = (Integer v) -> v < 10 && v > 5;
break;
case 2:
searchTerm = (Integer v) -> v >= 10 && v < 19;
break;
case 3:
searchTerm = (Integer v) -> v > 19 && v < 24;
break;
}
while (searchTerm.test(i)) {
...
}
You can create an enumeration as below.
public enum SearchTerms {
None(""),
Between6And9("i<10 && i>5"),
Between10And18("i>=10 && i<19"),
Between20And23("i>19 && i<24");
private final String stringValue;
SearchTerms(String stringValue) {
this.stringValue = stringValue;
}
public String getStringValue() {
return stringValue;
}
public static SearchTerms fromStringValue(String stringValue) {
for (SearchTerms searchTerm : values()) {
if (searchTerm.getStringValue().equalsIgnoreCase(stringValue)) {
return searchTerm;
}
}
return SearchTerms.None;
}
}
Usage:
SearchTerms searchTerm = SearchTerms.fromStringValue("i<10 && i>5");
switch(searchTerm) {
case Between6And9:
//dosomething
break;
}
You can use .eval() of JavaScript.
Also don't forget break; at the end of each case:
Check out this fiddle.
Here is the snippet.
function test(input, i) {
switch (input) { //input=1
case 1:
searchTerm = "i<10 && i>5"; //this will be 'searchTerm'
break;
case 2:
searchTerm = "i>=10 && i<19";
break;
case 3:
searchTerm = "i>19 && i<24";
break;
}
while (eval(searchTerm)) { //'searchTerm' converted to boolean expression
alert(i); // alert for i=7,8,9
i++;
}
}
test(1, 7); //pass input=1 and i=7
Is there a way to use relational operators (<,<=,>,>=) in a switch statement?
int score = 95;
switch(score) {
case (score >= 90):
// do stuff
}
the above example (obviously) doesn't work
No you can not.
From jls-14.11
The type of the Expression must be char, byte, short, int, Character, Byte, Short, Integer, String, or an enum type (§8.9), or a compile-time error occurs.
Relational operators (<,<=,>,>=) results in boolean and which is not allowded.
All of the following must be true, or a compile-time error occurs:
Every case constant expression associated with a switch statement must be assignable (§5.2) to the type of the switch Expression.
No two of the case constant expressions associated with a switch statement may have the same value.
No switch label is null.
At most one default label may be associated with the same switch statement.
This might help you if you need to do it with switch itself,
char g ='X';
int marks = 65;
switch(marks/10)
{
case 1:
case 2:
case 3:
case 4: g = 'F';
break;
case 5: g = 'E';
break;
case 6: g = 'D';
break;
case 7: g = 'C';
break;
case 8: g = 'B';
break;
case 9:
case 10: g = 'A';
break;
}
System.out.println(g);
It works this way,
if(marks<50)
g='F';
else if(marks<60)
g='E';
else if(marks<70)
g='D';
else if(marks<80)
g='C';
else if(marks<90)
g='B';
else if(marks<=100)
g='A';
Unfortunately NO, though you can use case fall (kind of hacky) by grouping multiple case statements without break and implement code when a range ends:
int score = 95;
switch(score) {
..
case 79: System.out.println("value in 70-79 range"); break;
case 80:
..
case 85: System.out.println("value in 80-85 range"); break;
case 90:
case 91:
case 92:
case 93:
case 94:
case 95: System.out.println("value in 90-95 range"); break;
default: break;
}
IMHO, using if would be more appropriate in your particular case.
It will never work. You should understand what switch does in the first place.
It will execute the statements falling under the case which matches the switch argument.
In this case, score is an argument which is 95 but score>=90 will always evaluate to either true or false and never matches an integer.
You should use if statements instead.
Also Java doesn't allow booleans in switch cases so yea.
Simply NO
int score = 95;
switch(score) {
case (score >= 90):
// do stuff
}
You are passing a int value to switch. So the case's must be in int values, where
(score >= 90)
Turns boolean.
Your case is a good candidaate for if else
The docs for switch-case statement state:
a switch statement tests expressions based only on a single integer, enumerated value, or String object.
So there is no boolean. Doing so would make no sence since you only have two values: true or false.
What you could do is write a method which checks the score and then returns a one of the types switch can handle
For example:
enum CheckScore {
SCORE_HIGHER_EQUAL_90,
...
}
public CheckScore checkScore(int score) {
if(score >= 90) {
return SCORE_HIGHER_EQUAL_90;
} else if(...) {
return ...
}
}
and then use it in your switch:
switch(checkScore(score)) {
case SCORE_HIGHER_EQUAL_90:
// do stuff
}
... Or You could just use if, else-if, else directly!
Obviously, this is not possible as a language construct. But, just for fun, we could implement it by ourselves!
public class Switch<T, V> {
public static interface Action<V> {
V run();
}
private final T value;
private boolean runAction = false;
private boolean completed = false;
private Action<V> actionToRun;
public Switch(T value) {
this.value = value;
}
static public <T, V> Switch<T, V> on(T value) {
return new Switch<T, V>(value);
}
public Switch<T, V> ifTrue(boolean condition) {
runAction |= condition;
return this;
}
public Switch<T, V> ifEquals(T other) {
return ifTrue(value.equals(other));
}
public Switch<T, V> byDefault(Action<V> action) {
this.actionToRun = action;
return this;
}
public Switch<T, V> then(Action<V> action) {
if (runAction && !completed) {
actionToRun = action;
completed = true;
}
return this;
}
public V getResult() {
if (actionToRun == null) {
throw new IllegalStateException("none of conditions matched and no default action was provided");
}
return actionToRun.run();
}
}
Switch accepts any value to switch on and then provides functionality to match over boolean conditions (ifTrue method) or by exact matches (ifEquals method). Providing a value to switch on is needed just for the latter feature.
After building the conditions, user invokes getResult to obtain the result.
For example, we could create a method that tells us what it thinks about our score:
String tellMeMyScore(int score) {
return Switch.<Integer, String> on(score).byDefault(new Action<String>() {
public String run() {
return "really poor score";
}
}).ifTrue(score > 95).then(new Action<String>() {
public String run() {
return "you rock!";
}
}).ifTrue(score > 65).then(new Action<String>() {
public String run() {
return "not bad, not bad";
}
}).ifEquals(42).then(new Action<String>() {
public String run() {
return "that's the answer!";
}
}).getResult();
}
This simple test:
for (int score : new int[] { 97, 85, 66, 55, 42, 32, 4 }) {
System.out.println(score + ": " + tellMeMyScore(score));
}
Prints out:
97: you rock!
85: not bad, not bad
66: not bad, not bad
55: really poor score
42: that's the answer!
32: really poor score
4: really poor score
I'm trying to create a method which checks if the Login (username and password) has a minimum of 6 charakters.
To realize that I created this method public void checkLoginData(final String username, final String password). In that method, I create to booleans (user and pass), with those I can create 4 different boolean-chains:
user: true pass: true
user: false pass: true
user: false pass: false
user: true pass: false
Now I'd like to do a switch/case request for each of them, but I don't get how to realize that...
If you ask why I need the switch, I just think I need it, because I'd like to do for every of those 4 boolean-chains, that it does/show something diffrent. Also I'd like to do this in a sexy-java-way not with tousands of diffrent 'ifs' :P, Please help!
Here's the code of the method:
public void checkLoginData(final String username, final String password){
boolean user, pass;
if (username.length() < 6){
user = false;
}else {
user = true;
}
if (password.length() < 6){
pass = false;
}else {
pass = true;
}
boolean[] logindaten = {user, pass};
}
Thx for the help in Advance!
Best Regards safari
If you really want a "sexy-java-way" (but that depends what you understand as such) you can do something like (Java 7 required):
boolean user, pass;
switch (user + "-" + pass) {
case "false-false":
...
case "false-true":
...
case "true-false":
...
case "true-true":
...
default:
throw new RuntimeException(
"something strange happening here, user: " + user + ",pass: " + pass);
}
but I would prefer to do just 2 distinct checks each with his owns message, the message being joined for presentation. (and not sure if that could be considered "sexy-java-way", more like a 'workaround')
You can't switch over boolean[], only over integral types. To convert the booleans to an int, you could use a bit mask for the 2 booleans, like for example this:
int val = 0;
if (user) val |= 0x1;
if (pass) val |= 0x2;
switch (val) {
case 0: // Both too short
case 1: // User Ok, pass too short
case 2: // User too short, pass ok
case 3: // Both Ok
}
Guess thats how I would solve it with enums:
public class LoginController
{
private void login( String username, String password )
{
LoginState state = determineLoginState( username, password );
switch ( state )
{
case LOGIN_OK:
//Do Something
break;
case USERNAME_FALSE:
//Do Something
break;
case PASSWORD_FALSE:
//Do Something
break;
case BOTH_FALSE:
//Do Something
break;
}
}
private LoginState determineLoginState( String username, String password )
{
final boolean checkUsername = checkUsername( username );
final boolean checkPassword = checkPassword( password );
if ( checkUsername && checkPassword )
return LoginState.LOGIN_OK;
if ( !checkUsername && checkPassword )
return LoginState.USERNAME_FALSE;
if ( checkUsername && !checkPassword )
return LoginState.PASSWORD_FALSE;
if ( !checkUsername && !checkPassword )
return LoginState.BOTH_FALSE;
throw new AuthenticationException();
}
protected boolean checkUsername( String username )
{
return username.length() > 6;
}
protected boolean checkPassword( String password )
{
return password.length() > 6;
}
private enum LoginState
{
LOGIN_OK, USERNAME_FALSE, PASSWORD_FALSE, BOTH_FALSE;
}
public class AuthenticationException extends RuntimeException
{
}
}
Basically there is no simpler way than this, and no way to do it in significantly less lines of code.
if (username.length() < 6){
if (password.length() < 6){
// do case 1
} else {
// do case 2
}
} else {
if (password.length() < 6){
// do case 3
} else {
// do case 4
}
}
To my mind, that makes this the best solution.
Also I'd like to do this in a sexy-java-way not with tousands of diffrent 'ifs'
If by "sexy-java-way" you mean "clever" or "obscure", then there are other ways to do it. But they certainly don't make the code easier to read / more maintainable.
By the way, the above involves only 3 ... that's right THREE ... if statements.
However your (final) specific example:
public void checkLoginData(final String username, final String password){
boolean user, pass;
if (username.length() < 6){
user = false;
}else {
user = true;
}
if (password.length() < 6){
pass = false;
}else {
pass = true;
}
boolean[] logindaten = {user, pass};
....
}
can be simplified to the following:
public void checkLoginData(final String username, final String password){
boolean user = username.length() >= 6;
boolean pass = password.length() >= 6;
boolean[] logindaten = {user, pass};
....
}
Note that simplification is possible here because the actions (the "cases" in your hypothetical switch) can be refactored into simple boolean assignments AND the tests are actually independent of each other. In general you can't do that ...
... but id like to have it more celver to impress my boss ;)
Seriously, if I was your boss and you wrote code like that, I'd be UN- impressed. Any boss who thinks you are clever for writing obscure and unmaintainable code is clueless.
if (user) {
if (pass) {
// user = true, pass = true
} else {
// user = true, pass = false
}
} else {
if (pass) {
// user = false, pass = true
} else {
// user = false, pass = false
}
}
Or
int case = user ? (pass ? 1 : 2) : (pass ? 3: 4);
switch (case) {
case 1:
System.out.println(" user = true, pass = true ");
break;
case 2:
System.out.println(" user = true, pass = false ");
break;
case 3:
System.out.println(" user = false, pass = true ");
break;
case 4:
System.out.println(" user = false, pass = false ");
break;
}
}
You can do something like this and then each case is 1,2 or 3, etc.
switch((route.isComplete()?1:(route.getAuthentic()?2:(route.hasRoute()?3:0)))) {...}
With java12, you can use expressions within switch-case and provide a Bool type (https://blog.codefx.org/java/switch-expressions/).
private static boolean checkCharOf_(String userName){
return userName.length() >= 6;
}
private static boolean checkCharOf_(String password){
return password.length() >= 6;
}
or
private static boolean checkCharOf_And_(String userName, String password){
return userName.length() >= 6 && password.length() >= 6;
}
I have looked through as many previous questions as possible but never saw a question that had a boolean array as a variable.
Here is my class:
public class Register {
private boolean[] register;
private int length;
//Normal constructor
public Register(int n) {
if (n == 8 || n == 16 || n == 32 || n == 64) {
length = n;
register = new boolean[length];
for (int i = 0; i < length; i++) {
register[i] = false;
}
} else {
throw new RegisterException(
"A register can only contain 8, 16, 32, or 64 bits");
}
}
// Creates a copy of reg (an existing Register)
public Register(Register reg) {
length = reg.length;
register = new boolean[reg.register.length];
System.arraycopy(reg.register, 0, this.register, 0, reg.register.length);
}
In my driver program i am loading "1101101" into register1, but when i do:
Register register2 = new Register(register1);
and print out both results i get:
0000000001101101
0000000000010110
Not really sure what is going on O.o any help would be appreciated, thanks!
This is my load method. i held off on putting it in here because it might be hard to read:
public void load(String binaryRep) {
String allTheBits = binaryRep;
int charPosition = 0;
int loadLength;
int binaryNum = 0;
String index = "";
String trimmedIndex = "";
if (allTheBits.length() > 0 && allTheBits.length() <= length) {
loadLength = allTheBits.length();
for (int i = length - (loadLength); i < length; i++) {
index = allTheBits.charAt(charPosition) + "";
trimmedIndex = index.trim();
binaryNum = Integer.parseInt(trimmedIndex);
if (binaryNum == 1) {
register[i] = true;
} else if (binaryNum == 0) {
register[i] = false;
}
charPosition++;
}
} else {
throw new RegisterException("You can only load 0 - " + length
+ "bits.");
}
}
Here's a more idiomatic way of doing it (using the Cloneable interface):
public class Register implements Cloneable {
private boolean[] register;
public Register(boolean[] register) {
int n = register.length;
if (n == 8 || n == 16 || n == 32 || n == 64) {
this.register = register;
} else {
throw new IllegalArgumentException(
"A register can only contain 8, 16, 32, or 64 bits");
}
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
for ( boolean b : this.register ) {
builder.append( b ? "1" : "0" );
}
return builder.toString();
}
public Register( int n ) {
this( new boolean[n] );
}
public int getLength() {
return this.register.length;
}
#Override
public Register clone() {
boolean[] clonedRegister = new boolean[this.register.length];
System.arraycopy(this.register, 0, clonedRegister,0, this.register.length);
return new Register( clonedRegister );
}
}
And a JUnit test showing it in action:
import org.junit.Assert;
import org.junit.Test;
public class RegisterTest {
#Test
public void testRegisterToString() {
Register source = new Register( new boolean[] {true, true, false, false, true, false, true, false } );
String result = "11001010";
Assert.assertEquals( result, source.toString() );
}
#Test
public void testRegisterCloning() {
Register source = new Register( new boolean[] {true, true, false, false, true, false, false, false } );
Register clone = source.clone();
Assert.assertEquals( source.toString(), clone.toString() );
}
}
A couple of remarks so that you learn some basic things.
As #Ted said, no need to keep the length field as register. length will give you the same
Local variables are not initialized with default values, but arrays are, as they are stored in the heap. So there's no need to iterate over the "register" array to set all of its positions to false
Using an array of booleans to do this may have felt easy but its extremely inefficient memory wise, as each boolean takes at least 32 bits in the heap. Therefore, to represent a 64 bit register you are using at least 32*64+32=2080 bits... using a byte array and bitwise logic will be a bit harder but hey, it's a small challenge :)
Anyway, your code looks fine (BTW, use Arrays.copyOf method as it's more readable), so the error should be coming from another side.
I just verified your load method with the following:
public static void main(String [] args)
{
Register r1 = new Register(8);
r1.load("1101101");
Register r2 = new Register(r1);
for (int i=0; i<8; i++) System.out.println(r2.register[i]);
}
Output:
> run Register
false
true
true
false
true
true
false
true
>
It looks right to me as far as the contents of the Register objects are concerned, so the problem probably is with the access.