Verwenden Sie ein Array als case-Anweisung in switch

76

Ich versuche so etwas zu tun, dh ein Array in einer switch-Anweisung zu verwenden. Ist es in Java möglich? Ist dies nicht der Fall, erläutern Sie bitte eine mögliche Lösung.

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;
}
Todor Grudev
quelle
5
Ich denke, am besten ist es, eine if-Anweisung zu verwenden und den Wechsel zu vermeiden
Mukul Goel
56
Ich denke, Sie sollten sich fragen, warum Ihre Ausführung von einem booleanArray abhängen sollte . Vielleicht könnte eine Klasse diese Daten besser mit Methoden enthalten, die mehr Semantik haben (und einfacher zu testen sind)? Wie Sie es geschrieben haben, sieht es aus wie ein zukünftiger Alptraum für die Wartung.
Eric Wilson
1
Verwenden Sie eine Schleife, um Bits auf einem int basierend auf dem Array-Index zu setzen, und schalten Sie diese aus.
Oktal9
15
Was Sie wollen, heißt "Pattern Matching", aber Sie können es nicht in Java tun.
Ingo
11
Es hört sich so an, als würden Sie versuchen, Bitflags
T. Kiley

Antworten:

65

NEIN , einfach kannst du nicht.

SwitchStatement:
    switch ( Expression ) SwitchBlock

Der Typ des Ausdrucks muss char, byte, short, int, Character, Byte, Short, Integer, String oder ein Aufzählungstyp (§8.9) sein. Andernfalls tritt ein Fehler bei der Kompilierung auf.

http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.11

Suresh Atta
quelle
Dies ist für mich richtig, obwohl einige gute Bemühungen in den anderen Antworten für eine gute Lösung.
Code Whisperer
8
Ehrlich gesagt ist dies die beste Antwort. Ich bin mir nicht sicher, ob die Art der Gymnastik, die erforderlich ist, um das ursprüngliche Problem zu lösen, gefördert werden sollte.
NotMe
Das ist wirklich alt und ich kann mich nicht einmal daran erinnern, was das ursprüngliche Problem war, aber wenn ich auf den Code zurückblicke, scheint es, dass ich eine schlechte architektonische Entscheidung getroffen habe. Deshalb denke ich, dass dies die richtige Antwort ist - es ist eher ein Signal, etwas falsch zu machen, als eine Lösung zu finden.
Todor Grudev
74

@ sᴜʀᴇsʜ ᴀᴛᴛᴀ ist richtig. Aber ich wollte etwas hinzufügen. Seit Java 7 unterstützen switch-Anweisungen Strings, sodass Sie damit etwas anfangen können. Es ist wirklich schmutzig und ich empfehle nicht, aber das funktioniert:

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

Für diejenigen, die sich Gedanken über die Leistung machen: Sie haben Recht, das ist nicht super schnell. Dies wird in etwa so zusammengestellt:

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;
}
Martijn Courteaux
quelle
Ich bin ziemlich neutral + -1. Es wäre sehr langsam. Ich dachte auch nicht, dass Sie die Ausgabe in irgendeiner Weise analysieren sollten. toString
Bathsheba
72
Kreativ und schrecklich zugleich. +1
David
3
Zu beschränkt auf die Größe des Arrays, wenn sich die Größe des Arrays ändert, was für ein Durcheinander es zu pflegen ist.
Jonathan Drapeau
1
Es gibt diese blasierte Haltung gegenüber der Leistung. Diese Art von Problem wird sehr häufig durch das Setzen von Bits in a gelöstint - Sie konvertieren jedoch in Zeichenfolgen und vergleichen diese . In Java .
Bobobobo
1
@ Bobobobo: Du hast recht. Ich werde das niemals für mich selbst schreiben. Dies war nur ein lustiger Code, der sehr nahe an dem liegt, was das OP wollte. Ich weiß, dass das schrecklich ist, vertrau mir :)
Martijn Courteaux
50

Sie können nicht ganze Arrays einschalten. Sie können jedoch auf Kosten der Lesbarkeit switchselbst in ein Bit konvertieren :

switch (values[0] + 2 * values[1] + 4 * values[2] + 8 * values[3])

und verwenden Sie binäre Literale in Ihren case-Anweisungen: case 0b0101ist Ihre erste.

Bathseba
quelle
Wow, das ist sicherlich möglich, aber ist das eine gute Lösung für das eigentliche Problem?
Eric Wilson
5
Um dies erweiterbar zu machen, würde ich es in eine Schleife setzen: int switchVal = 0; for (int i = 0; i < values.length(); i++) { switchVal += values[i] ? (2 << i) : 0; }und dann die switchValVariable einschalten .
Darrel Hoffman
3
Um den Schalter lesbarer / aussagekräftiger zu machen, sollten Sie erwägen, jedes dieser binären Literale zu einer aussagekräftigen Konstante zu machen, damit Sie Konstanten und keine binären Literale einschalten.
Rrauenza
3
Dies ist im Grunde eine Konvertierung [true, false, true, false]in eine Reihe von Bits 1010, die bearbeitet werden können switch, oder? Auf jeden Fall der Weg zu IMO.
Izkata
Ich wollte ein binäres Beispiel mit Lambda in Java SE 8 zeigen, war mir aber nicht sicher, ob ich dafür in Flammen stehen würde. Ich werde bis März warten.
Ialexander
47

Versuchen Sie diese Lösung:

    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 {
    ...
    }

Siehe Dokumente hier .

Alex
quelle
50
Dies ist eine Lösung, die bis zur Größe des Arrays sehr eng ist. Sobald sich die Größe des Arrays ändert, haben Sie einen Alptraum bei der Wartung.
Jonathan Drapeau
98
Sie nannten eine Funktion isEquals?
Sebastian Mach
10
Apache-commons-lang ist nicht erforderlich - verwenden Sie es einfach java.util.Arrays.equals(arr1,arr2)aus dem JDK.
Ed Staub
10
@ Drigoangelo: Ich weiß, ich weiß. Aber ich frage mich, warum sie nicht einfach isEqualso verb adjectiveoder so verb nounstatt verb verb. Persönlich bevorzuge ich nur adjectiveboolesche Funktionen und verbProzeduren, die den Status ändern.
Sebastian Mach
2
@ JonathanDrapeau, Sie werden wahrscheinlich sowieso einen Wartungsalptraum mit dieser Art von Design haben ...
MEMark
22

Ja, Sie können ein Array an einen Switch übergeben. Der Haken ist, dass ich nicht über Java-Arrays spreche, sondern über eine Datenstruktur.

Ein Array ist eine systematische Anordnung von Objekten, normalerweise in Zeilen und Spalten.

Sie versuchen, ein System zu implementieren, das unterschiedliche Flags erkennt, und abhängig von den ein- oder ausgeschalteten Flags führen Sie unterschiedliche Aktionen aus.

Beispiel

Eine beliebte Implementierung eines solchen Mechanismus sind Linux-Dateiberechtigungen. Wo Sie rwxals "Array von Flags" haben.

Wenn das gesamte Array wahr ist, werden Sie sehen rwx, was bedeutet, dass Sie über alle Berechtigungen verfügen. Wenn Sie keine Aktion für eine Datei ausführen dürfen, ist das gesamte Array falsch ---.

Implementierung

Ratet mal, Sie können sich ganze Zahlen als Arrays vorstellen. Eine Ganzzahl wird durch ein "Array von Bits" dargestellt.

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

Deshalb rwxkann die Erlaubnis als dargestellt werden7

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

Um mehr über die Verwendung eines Binärformats zu erfahren, überprüfen Sie diese Frage: Kann ich in Java eine Ganzzahlkonstante im Binärformat definieren?

Performance

  • Spart Speicher
  • Sie müssen keine zusätzliche Verarbeitung, Schalter oder andere Arten des Jonglierens durchführen.

C-Programme, die so effizient wie möglich sein müssen, verwenden diese Art von Mechanismus. Sie verwenden Flags, die mit einzelnen Bits dargestellt werden.

givanse
quelle
21

Nein, das können Sie nicht, aber Sie können das Obige durch den folgenden (ich gebe zu) schmutzigen Code ersetzen:

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);
}
Adam Siemion
quelle
3
Ich würde die Summenformel in eine Methode einfügen, anstatt direkt in die switchAnweisung.
Jonathan Drapeau
8
Abgesehen von der Tatsache, dass es äh hässlich ist, ist es falsch: 0010wird als oktal interpretiert ...
Gyro Gearless
2
@Alex-Hilfsmethode sollte sein private;) Auch ist es keine gute Idee, return intda dies bei großen Arrays überlaufen kann.
Maroun
1
Ich mag dies, soweit es so aussieht, als wäre es einfacher, gegen Mammut zu behaupten, wenn / sonst Aussagen in der akzeptierten Antwort
WernerCD
1
Und warum, (values[0]?1:0)*1000wenn du kannst values[0]?1000:0?
Cruncher
10

Wenn Sie feststellen möchten, ob eine Reihe von Bedingungen erfüllt ist, verwende ich stattdessen bitweise Felder.

Zum Beispiel,

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 ;
    }
  }
}
Bobobobo
quelle
6

Ich würde einen Wert basierend auf der Reihenfolge der Elemente im booleschen Array berechnen, dh [true, false, true, true]1011 auswerten und dann basierend auf diesem ganzzahligen Wert die switch-Anweisung verwenden.

Juvanis
quelle
Stellen Sie sicher, dass das Ändern der Länge (oder Reihenfolge) des Arrays weiterhin funktioniert, wenn Sie vermeiden möchten, dass jede 'case'-Anweisung neu geschrieben wird. Wenn Java keinen Funktionsaufruf für jeden 'Fall' zulässt, ist eine massive if-Kette erforderlich, möglicherweise mit einem assoziativen Array für jeden alternativen Fall und einem Funktionsaufruf für jeden if-Test.
Phil Perry
2

Die Antwort ist nein. Am besten lernen Sie, wie Sie die switch-Anweisung verwenden .

Sergi
quelle
Switch-Anweisungen akzeptieren nur Ints, Zeichen, Bytes, Shorts und Aufzählungstypen. Der "richtige" Weg, dies zu tun, wäre entweder ein großer if..else-Block oder das Array von Booleschen Werten auf eine ganze Zahl zu rendern und entsprechend zu handeln. Vielleicht als Bitmaske behandeln?
David
1
Java-Switches nehmen auch Strings
Bathsheba
2
@Bathsheba Für Java7 +.
Maroun
2

Ab JRE 1.7 müssen Sie einen Hack verwenden. Ich empfehle:

  • Annehmen values.length <= 64

  • Konvertieren Sie Werte in longdarstellende Bitflags

  • Switchgegen hexadezimale magische Zahlen

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;
}
recursion.ninja
quelle
2

Diese Antwort ist nicht Java , sondern Haxe, weil es dank Pattern Matching möglich ist und eine interessante Ausgabe hat, die für Sie nützlich sein kann, um einen Switch zu finden, der das tut, was Sie verlangen. Arrays können auf feste Länge abgestimmt werden.

Ich habe eine Demo erstellt, die in Javascript und Flash kompiliert werden kann. Sie können die js-Ausgabe in der rechten Spalte sehen.

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"
  }
}

Dies ist der ausgegebene Switch, der verschachtelte Switches verwendet. Wenn Sie mit den Fällen spielen, sehen Sie, wie sich die js-Ausgabe ändert, um einen effizienten Schalter zu haben.

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

Ein weiteres interessantes Muster, das Sie mit Unterstrichen versehen können. Ein _-Muster stimmt mit allem überein, daher entspricht case _: dem Standardwert, sodass Sie Folgendes tun können:

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

Mark Knol
quelle
1

Hier ist ein weiterer Ansatz, der weder Importe noch Bibliotheken erfordert:

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);
}
Stephan
quelle
1

Sie können sich auch ansehen, wie Groovy die isCase () -Methoden in Java implementiert. Verwenden Sie eine einfachere Version, die Ihren Anforderungen entspricht. Es ist möglich, dies in eine Schnittstelle einzufügen und eine DSL zu erstellen, um zwei beliebige Objekte in Ihrer Anwendung zu vergleichen.

return isCase(DefaultTypeTransformation.asCollection(caseValue), switchValue);

Der relevante Code wird in den Zeilen 877 bis 982 behandelt

rgamed
quelle
1

@Todor Ja, das ist in Java möglich.

boolean[] values = new boolean[4];

values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;

values = Arrays.toString(values)

switch (values) {
    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;
}

Hinweis: Ich bin kein Java-Entwickler, daher kann meine Codesyntax falsch sein, aber die Logik ist perfekt. Sie können meine Antwort bearbeiten. Hier habe ich nur versucht, das Array in das String-Format zu konvertieren und es dann im Switch-Fall abzugleichen.

Jatinkumar Patel
quelle
0

Ich würde konstante int-Werte verwenden, die den booleschen Zustand darstellen.

Wenn Sie Java 1.7 oder höher verwenden, können Sie besser lesbare Binärliterale verwenden .

public static final int TRUE_FALSE_TRUE_FALSE = 0b1010;
public static final int FALSE_FALSE_TRUE_FALSE = 0b0010;

Verwenden Sie für Java 1.6 und niedriger andere int-Literale, z. B. hex

public static final int TRUE_FALSE_TRUE_FALSE = 0xA;
public static final int FALSE_FALSE_TRUE_FALSE = 0x2;

Erstellen Sie dann eine Methode, die ein boolesches Array in ein ganzzahliges Bitset konvertiert. Z.B

public static int toIntBitSet(boolean...values){
    int bitset = 0;
    for (boolean value : values) {
       bitset = (bitset << 1) | (value ? 1 : 0);
    }
    return bitset;
}

Verwenden Sie schließlich die Konstanten in Ihrer switch-Anweisung

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

Ein anderer Ansatz könnte darin bestehen, ein Java BitSetund ein Java zu verwenden Map, das der Logik zugeordnet ist, die abhängig vom Wert des Bitsets ausgeführt werden soll.

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

und implementieren Sie Ihre Logik

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

}
René Link
quelle