Utilizați o matrice ca instrucțiune case în switch (Programare, Java, Array-Uri, Declarație De Comutare)

Todor Grudev a intrebat.

Încerc să fac ceva de genul acesta, adică să folosesc un array într-o declarație switch. Este posibil în Java? Dacă nu este, vă rog să explicați o posibilă soluție.

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

Comentarii

  • Cred că cel mai bine este să folosiți o instrucțiune if și să evitați comutatorul –  > Por Mukul Goel.
  • 56

  • Cred că ar trebui să vă întrebați de ce execuția dvs. ar trebui să depindă de un boolean array. Poate că o clasă ar putea conține mai bine aceste date cu metode care au mai multă semantică (și sunt mai ușor de testat)? Așa cum ați scris-o, pare un coșmar de întreținere viitoare. –  > Por Eric Wilson.
  • Folosiți o buclă pentru a seta biți pe un int pe baza indicelui array-ului și dezactivați acest lucru. –  > Por octal9.
  • 15

  • Ceea ce doriți se numește „potrivire de tipare”, dar nu o puteți face în Java. –  > Por Ingo.
  • Se pare că încercați să implementați indicatori de biți – consultați această întrebare: Implementarea unui câmp de biți utilizând java enums –  > Por T. Kiley.
15 răspunsuri
Suresh Atta

NU, , pur și simplu nu se poate.

SwitchStatement:
    switch ( Expression ) SwitchBlock

Tipul Expresiei trebuie să fie char, byte, short, int, Character, Byte, Short, Integer, String sau un tip enum (§8.9), altfel apare o eroare de compilare.

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

Comentarii

  • Din punctul meu de vedere, acest lucru este corect, deși în celelalte răspunsuri se fac eforturi bune pentru o soluție bună. –  > Por Code Whisperer.
  • Sincer, acesta este cel mai bun răspuns. Nu sunt sigur că ar trebui încurajat tipul de gimnastică necesar pentru a face ca problema originală să funcționeze. –  > Por NotMe.
  • Acest lucru este foarte vechi și nici măcar nu-mi amintesc care a fost problema inițială, dar uitându-mă înapoi la cod se pare că am luat o decizie arhitecturală proastă. De aceea, cred că acesta este răspunsul corect – este mai degrabă un semnal de a face ceva greșit decât de a găsi o soluție. –  > Por Todor Grudev.
Martijn Courteaux

@sᴜʀᴇsʜ ᴀᴛᴛᴛᴀ are dreptate. Dar am vrut să adaug ceva. Începând cu Java 7, declarațiile switch suportă Strings, așa că ați putea face ceva cu asta. Este foarte murdar și nu recomand, dar acest lucru funcționează:

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

Pentru cei care sunt îngrijorați de performanță: aveți dreptate, acest lucru nu este super rapid. Acest lucru va fi compilat în ceva de genul acesta:

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

Comentarii

  • Sunt destul de neutru +-1. Ar fi foarte lent. De asemenea, nu am crezut că ar trebui să se să analizați toString în orice mod. –  > Por Bathsheba.
  • 73

  • Creativ și teribil în același timp. +1 –  > Por David.
  • Prea restricționat la dimensiunea matricei, dacă matricea ca să se schimbe în dimensiune, ce mizerie este să o întreții. –  > Por Jonathan Drapeau.
  • Există această atitudine blazată față de performanță. Acest tip de problemă este foarte frecvent rezolvată prin setarea unor biți într-un int — totuși, se convertește și se compară șiruri de caractere. În Java. –  > Por bobobobobo.
  • @bobobobobobo: Aveți dreptate. Niciodată nu voi scrie asta pentru mine. Aceasta a fost doar o bucată amuzantă de cod care se află foarte aproape de ceea ce a vrut OP. Știu că acest lucru este oribil, credeți-mă 🙂 –  > Por Martijn Courteaux.
Bathsheba

Nu poți activa array-uri întregi. Dar puteți ați putea converti la un set de biți, în detrimentul unei anumite lizibilități de citire a switch în sine:

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

și să folosiți literali binari în declarațiile de caz: case 0b0101 este prima.

Comentarii

  • Wow, acest lucru este cu siguranță posibil, dar este aceasta o soluție bună la adevărata problemă? –  > Por Eric Wilson.
  • Pentru a face acest lucru extensibil, l-aș pune într-o buclă: int switchVal = 0; for (int i = 0; i < values.length(); i++) { switchVal += values[i] ? (2 << i) : 0; } și apoi porniți switchVal variabilă. –  > Por Darrel Hoffman.
  • Și pentru ca comutarea să fie mai ușor de citit/semnificativă, luați în considerare posibilitatea de a face din fiecare dintre aceste literale binare o constantă semnificativă, astfel încât să comutați pe constante, nu pe literale binare. –  > Por rrauenza.
  • Practic, acest lucru înseamnă convertirea [true, false, true, false] în seria de biți 1010, , care poate fi switched pe, nu-i așa? Cu siguranță este calea de urmat IMO. –  > Por Izkata.
  • Am vrut să arăt un exemplu binar folosind Lambda în Java SE 8, dar nu eram sigur dacă voi fi atacat pentru asta. Voi aștepta până în martie. –  > Por ialexander.
Alex

Încearcă această soluție:

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

Vezi documentele aici.

Comentarii

    50

  • Aceasta este o soluție foarte strânsă până la dimensiunea array-ului, în momentul în care array-ul își va schimba dimensiunea, veți avea un coșmar de întreținere. –  > Por Jonathan Drapeau.
  • 98

  • Au numit o funcție isEquals?? –  > Por Sebastian Mach.
  • Nu este nevoie de apache-commons-lang – folosiți doar java.util.Arrays.equals(arr1,arr2) din JDK. –  > Por Ed Staub.
  • @drigoangelo: Știu, știu. Dar mă întreb de ce nu au folosit pur și simplu isEqual, , astfel încât fie verb adjective sau verb noun, , în loc de verb verb. Personal, eu prefer adjective pentru funcțiile booleene, iar verb pentru procedurile care schimbă starea. –  > Por Sebastian Mach.
  • @JonathanDrapeau, oricum veți avea probabil un coșmar de întreținere cu acest tip de design… –  > Por MEMark.
givanse

Da, puteți trece o matrice către un comutator. Capcana este că nu mă refer la array-uri Java, ci la o structură de date.

O matrice este un aranjament sistematic de obiecte, de obicei în rânduri și coloane.

Ceea ce încercați să faceți este să implementați un sistem care să recunoască diferite stegulețe și, în funcție de stegulețele care sunt activate sau dezactivate, să întreprindeți diferite acțiuni.

Exemplu

O implementare populară a unui astfel de mecanism este reprezentată de permisiunile fișierelor Linux. În cazul în care aveți rwx ca „matrice de indicatori”.

Dacă întreaga matrice este adevărată, veți vedea rwx, , ceea ce înseamnă că aveți toate permisiunile. Dacă nu vi se permite să efectuați nicio acțiune asupra unui fișier, întreaga matrice este falsă, veți vedea ---.

Implementare

Ghiciți ce, vă puteți gândi la numere întregi ca la un array. Un număr întreg este reprezentat de un „array de biți”.

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

Acesta este motivul pentru care permisiunea rwx poate fi reprezentat ca un 7

fragment Java:

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
"); break;         
            case 7: /* grant all permissions */ break;                           
            default:                                                             
                System.out.println("invalid flag
");           
        }                                                                        
    }                                                                            
}

Pentru a afla mai multe despre utilizarea unui format binar, consultați această întrebare: În Java, pot defini o constantă întreagă în format binar?

Performanță

  • Economisește memorie
  • Nu trebuie să faceți prelucrări suplimentare, comutații sau orice alt tip de jonglerii.

Programele C care necesită să fie cât mai eficiente folosesc acest tip de mecanism; ele folosesc stegulețe reprezentate cu biți simpli.

Adam Siemion

Nu, nu se poate, însă puteți înlocui codul de mai sus cu următorul cod (murdar, recunosc):

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

Comentarii

  • Eu aș pune formula de adunare într-o metodă în loc să o pun direct în switch declarație. –  > Por Jonathan Drapeau.
  • În afară de faptul că este errr urât, este greșit: 0010 este interpretată ca octal… –  > Por Gyro Gearless.
  • @Alex metoda de ajutor ar trebui să fie private 😉 De asemenea, nu este o idee bună să return int deoarece pentru array-uri mari acest lucru ar putea să se supraîncarce. –  > Por Maroun.
  • Îmi place acest lucru, în măsura în care se pare că ar fi mai ușor de întreținut decât declarațiile mamut if/else din răspunsul acceptat –  > Por WernerCD.
  • De asemenea, de ce (values[0]?1:0)*1000 dacă puteți face values[0]?1000:0? –  > Por Cruncher.
bobobobobo

Dacă încercați să determinați dacă un set de condiții este adevărat, aș folosi în schimb câmpuri bitwise.

De exemplu,

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

Juvanis

aș calcula o valoare pe baza secvenței de elemente din matricea booleană, și anume [true, false, true, true] ar fi evaluat la 1011 și apoi, pe baza acestei valori întregi, puteți utiliza instrucțiunea switch.

Comentarii

  • Asigurați-vă că, dacă schimbați lungimea (sau ordinea) array-ului, acesta funcționează în continuare, dacă doriți să evitați rescrierea fiecărei instrucțiuni „case”. În cazul în care Java nu permite un apel de funcție pentru fiecare „caz”, va fi necesar un lanț masiv if, eventual cu un tablou asociativ pentru fiecare caz alternativ și un apel de funcție pentru fiecare test if. –  > Por Phil Perry.
Sergi

Răspunsul este NU. Cea mai bună explicație este să înveți cum să folosești instrucțiunea switch.

Comentarii

  • Instrucțiunile switch acceptă doar tipurile ints, chars, bytes, shorts și enum. Modul „corect” de a face acest lucru ar fi fie un bloc mare if..else, fie să transformați cumva matricea de booleeni într-un întreg și să acționați în consecință. Poate că ar trebui să o tratăm ca pe o mască de biți? –  > Por David.
  • Comutatoarele Java acceptă și șiruri de caractere –  > Por Bathsheba.
  • @Bathsheba Pentru Java7+. –  > Por Maroun.
recursion.ninja

Începând cu JRE 1.7, va trebui să folosiți un hack, vă recomand:

  • Presupuneți că values.length <= 64

  • Convertiți valorile într-un long reprezentând bitflags

  • Switch față de hexazecimal numere magice

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

Mark Knol

Acest răspuns este nu Java, , ci Haxe, deoarece este posibil în el, datorită potrivirii de tipare și are o ieșire interesantă, care ar putea fi utilă pentru a găsi un comutator care să facă ceea ce cereți. Array-urile pot fi potrivite pe lungime fixă.

Am creat un demo care se compilează în Javascript și Flash. Puteți vedea ieșirea js în coloana din dreapta.

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

Acesta este comutatorul ieșit, folosește comutatoare imbricate. Dacă vă jucați cu cazurile, vedeți cum se schimbă js-ouput-ul pentru a avea un switch eficient.

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

Un alt model interesant pe care îl puteți utiliza este cel al sublinierilor. un model _ se potrivește cu orice, astfel încât cazul _: este egal cu implicit, ceea ce vă permite să faceți acest lucru:

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

Stephan

Iată o altă abordare care nu necesită importuri și nici biblioteci:

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

rgamed

Puteți, de asemenea, să aruncați o privire la modul în care Groovy implementează metodele isCase() din Java, utilizați o versiune mai simplă care se potrivește nevoilor dvs. Este posibil să puneți asta într-o interfață și să creați un DSL pentru a compara orice două obiecte din aplicația dvs.

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

Codul relevant este acoperit în Liniile 877 până la Liniile 982

jatinkumar patel

@Todor Da, acest lucru este posibil în JAVA.

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

Notă: Nu sunt un dezvoltator java, așa că sintaxa codului meu poate fi greșită, Dar logica este perfectă. Puteți edita răspunsul meu. Aici am încercat doar să convertesc matricea în format șir de caractere și apoi să se potrivească în cazul comutatorului.

René Link

Aș folosi valori int constante care reprezintă starea booleană.

Dacă utilizați Java 1.7 sau mai sus puteți utiliza literali binari care sunt mai ușor de citit.

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

pentru Java 1.6 și mai jos, folosiți orice alt literar int, de exemplu, hexagonal.

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

apoi creați o metodă care să convertească o matrice booleană într-un set de biți întregi. De ex.

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

În cele din urmă, utilizați constantele în instrucțiunea switch

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

O altă abordare ar putea fi utilizarea unei metode java BitSet și un Map care se referă la logica care trebuie executată în funcție de valoarea setului de biți.

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

și implementați logica dvs.

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

}