web-dev-qa-db-fra.com

Utiliser un tableau comme instruction de casse dans switch

J'essaie de faire quelque chose comme ceci, c'est-à-dire d'utiliser un tableau dans une instruction switch. Est-ce possible en Java? Si ce n'est pas le cas, veuillez expliquer une solution possible.

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;
}
73
Todor Grudev

NON, simplement vous ne pouvez pas.

SwitchStatement:
    switch ( Expression ) SwitchBlock

Le type de l'expression doit être caractère, octet, court, entier, caractère, octet, court, entier, chaîne ou un type enum (§8.9), sinon une erreur de compilation se produit. 

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

62
Suresh Atta

@ sᴜʀᴇsʜ ᴀᴛᴛᴀ a raison. Mais je voulais ajouter quelque chose. Depuis Java 7, les instructions switch supportent Strings, vous pouvez donc faire quelque chose avec cela. Il est vraiment sale et je ne recommande pas, mais cela fonctionne:

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

Pour ceux qui sont préoccupés par les performances: vous avez raison, ce n’est pas très rapide. Ce sera compilé dans quelque chose comme ça:

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;
}
73
Martijn Courteaux

Vous ne pouvez pas activer des tableaux entiers. Mais vous pourriez convertir en un bit défini au détriment de la lisibilité de la switch elle-même:

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

et utilisez littéraux binaires dans vos déclarations de cas: case 0b0101 est votre premier.

50
Bathsheba

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

Voir docs ici .

47
Alex

Oui, vous pouvez passer un tableau à un commutateur. Le problème, c'est que je ne parle pas de tableaux Java, mais d'une structure de données.

Un tableau est un arrangement systématique d'objets, généralement en lignes et en colonnes.

Ce que vous essayez de faire est de mettre en œuvre un système qui reconnaît différents indicateurs et, en fonction des indicateurs activés ou désactivés, vous entreprenez différentes actions.

Exemple

Une implémentation populaire de ce mécanisme est les permissions de fichiers Linux. Où vous avez rwx comme "tableau de drapeaux". 

Si tout le tableau est vrai, vous verrez rwx, ce qui signifie que vous avez toutes les autorisations. Si vous n'êtes autorisé à effectuer aucune action sur un fichier, si tout le tableau est faux, vous verrez ---.

La mise en oeuvre

Devinez quoi, vous pouvez considérer les nombres entiers comme des tableaux. Un entier est représenté par un "tableau de 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

C’est pourquoi l’autorisation rwx peut être représentée par un 7

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

Pour en savoir plus sur l’utilisation d’un format binaire, vérifiez la question suivante: En Java, puis-je définir une constante d’entier au format binaire?

Performance

  • Enregistre de la mémoire
  • Vous n'avez pas besoin de traitement supplémentaire, de commutateurs ou de tout autre type de jonglage.

Les programmes en C qui doivent être aussi efficaces que possible utilisent ce type de mécanisme; ils utilisent des drapeaux représentés avec des bits simples.

21
givanse

Non, vous ne pouvez pas, mais vous pouvez remplacer ce qui précède par le code suivant (sale, je l'admets)

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);
}
20
Adam Siemion

Si vous essayez de déterminer si un ensemble de conditions est vrai, j'utiliserais plutôt des champs au niveau du bit.

Par exemple,

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

Je calculais une valeur en fonction de la séquence des éléments du tableau booléen, c'est-à-dire que [true, false, true, true] serait évalué à 1011, puis en fonction de cette valeur entière, vous pouvez utiliser l'instruction switch.

6
Juvanis

La réponse est non. La meilleure explication est d'apprendre à utiliser l'instruction switch .

2
Sergi

À partir de JRE 1.7, vous devrez utiliser un hack, je recommande:

  • Supposer que values.length <= 64 

  • Convertir des valeurs en un long représentant des bitflags

  • Switch contre hexadécimal nombres magiques

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

Voici une autre approche ne nécessitant ni importations ni bibliothèques:

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

Cette réponse est pas Java, mais Haxe parce que c'est possible, grâce au filtrage et à la sortie intéressante, ce qui peut vous être utile pour trouver un commutateur qui fasse ce que vous demandez. Les tableaux peuvent être appariés sur une longueur fixe.

J'ai créé une démo qui compile en Javascript et en Flash. Vous pouvez voir le js-output dans la colonne de droite. 

Démo: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"
  }
}

C'est le commutateur de sortie, il utilise des commutateurs imbriqués. Si vous jouez avec les cas, vous voyez comment le js-ouput change pour avoir un switch efficace.

(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 autre motif intéressant que vous pouvez utiliser des traits de soulignement. un motif _ correspond à quoi que ce soit, donc la casse _: est égale à la valeur par défaut, ce qui vous permet de faire ceci:

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

1
Mark Knol

@Todor Oui, CECI IS EST POSSIBLE EN 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;
}

Remarque: je ne suis pas un développeur Java, ma syntaxe de code peut donc être incorrecte, mais la logique est parfaite. Vous pouvez modifier ma réponse. Ici, je viens d'essayer de convertir tableau en format de chaîne, puis correspondre en cas de commutation.

1
jatin

Vous pouvez également regarder comment Groovy implémente les méthodes isCase () en Java, utilisez une version plus simple qui répond à vos besoins. Il est possible de mettre cela dans une interface et de créer un DSL pour comparer deux objets quelconques dans votre application.

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

Le code correspondant est couvert dans Lignes 877 à travers Lignes 982

1
rgamed

J'utiliserais des valeurs int constantes qui représentent l'état booléen.

Si vous utilisez Java 1.7 ou une version ultérieure, vous pouvez utiliser des littéraux binaires qui sont plus lisibles .

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

pour Java 1.6 et inférieur, utilisez n’importe quel autre littéral int, par exemple. hexagone 

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

puis créez une méthode qui convertit un tableau booléen en un jeu de bits entier. Par exemple.

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

Enfin, utilisez les constantes dans votre instruction 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;
}

Une autre approche pourrait consister à utiliser un Java BitSet et un Map qui correspondent à la logique à exécuter en fonction de la valeur du bitet.

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

et mettre en œuvre votre logique

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

}
0
René Link