Використовуйте масив як оператор регістру в switch


76

Я намагаюся зробити щось подібне, тобто використовувати масив у операторі switch. Чи можливо це на Java? Якщо це не так, будь ласка, поясніть можливе рішення.

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

5
Я думаю, що найкраще використовувати оператор if і уникати перемикання
Mukul Goel

56
Я думаю, що вам слід запитати себе, чому ваше виконання має залежати від booleanмасиву. Можливо, клас міг би краще містити ці дані за допомогою методів, які мають більше семантики (і їх легше перевірити)? Як ви вже писали, це виглядає як кошмар майбутнього технічного обслуговування.
Ерік Вілсон,

1
Використовуйте цикл, щоб встановити біти для int на основі індексу масиву, і вимкніть це.
восьмерик9

15
Те, що ви хочете, називається "збігом зразків", але ви не можете зробити це на Java.
Інго

11
Здається, ви намагаєтесь реалізувати бітові прапори - див. Це запитання: Реалізація бітового поля за допомогою перерахувань Java
T. Kiley

Відповіді:


65

НІ , просто ти не можеш.

SwitchStatement:
    switch ( Expression ) SwitchBlock

Тип виразу повинен бути char, byte, short, int, Character, Byte, Short, Integer, String, або enum type (§8.9), або виникає помилка під час компіляції.

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


Що стосується мене, це правильно, хоча в інших відповідях є гарні зусилля для хорошого рішення.
Code Whisperer

8
Чесно кажучи, це найкраща відповідь. Я не впевнений, що тип гімнастики, необхідний для того, щоб спрацювати оригінальна проблема, слід заохочувати.
NotMe

Це насправді давно, і я навіть не можу згадати, що було початковою проблемою, але, озираючись на код, здається, що я зробив якесь погане архітектурне рішення. Ось чому я вважаю, що це правильна відповідь - це скоріше сигнал про те, що щось робиться неправильно, а не про пошук рішення.
Тодор Грудєв

74

@ sᴜʀᴇsʜ ᴀᴛᴛᴀ має рацію. Але я хотів щось додати. Оскільки Java 7, оператори switch підтримують рядки, щоб ви могли щось з цим зробити. Це дійсно брудно, і я не рекомендую, але це працює:

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

Для тих, кого турбує продуктивність: ви маєте рацію, це не надто швидко. Це буде скомпільовано приблизно так:

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

Я досить нейтральний + -1. Це було б дуже повільно. Крім того, я не думав, що ви повинні будь-яким чином проаналізувати toString вихідні дані .
Вірсавія

72
Творчий і страшний одночасно. +1
Девід

3
Занадто обмежений розміром масиву, якщо масив змінюється в розмірі, який безлад він повинен підтримувати.
Джонатан Драпо

1
Є таке блазне ставлення до продуктивності. Цей тип проблеми дуже часто вирішується встановленням бітів в int- але ви перетворюєте та порівнюєте рядки . На Java .
bobobobo

1
@bobobobo: Ви маєте рацію. Я ніколи не напишу це для свого власного. Це був просто кумедний шматок коду, який дуже близький до того, що хотів ОП. Я знаю, що це жахливо, повірте мені :)
Martijn Courteaux

50

Ви не можете вмикати цілі масиви. Але ви можете перетворити на біт, встановлений за рахунок певної читабельності switchсамого:

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

і використовуйте двійкові літерали у ваших твердженнях case: case 0b0101це ваш перший.


Ого, це, звичайно, можливо, але чи є це хорошим рішенням справжньої проблеми?
Ерік Вілсон,

5
Щоб зробити це розширюваним, я б помістив його в цикл: int switchVal = 0; for (int i = 0; i < values.length(); i++) { switchVal += values[i] ? (2 << i) : 0; }а потім увімкнув switchValзмінну.
Даррел Хоффман,

3
А щоб зробити перемикач більш читабельним / значущим, розгляньте можливість зробити кожен із цих двійкових літералів значущою константою, щоб ви вмикали константи, а не двійкові літерали.
rrauenza

3
Це в основному перетворюється [true, false, true, false]на серію бітів 1010, які можна switchредагувати, так? Безумовно, шлях до IMO.
Izkata

Я хотів показати двійковий приклад з використанням лямбди в Java SE 8, але не був впевнений, що мене це спалахне. Зачекаю до березня.
ialexander

47

Спробуйте це рішення:

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

Документи дивіться тут .


50
Це рішення дійсно жорстке до розміру масиву. У той момент, коли масив зміниться в розмірі, у вас буде кошмар на обслуговування.
Джонатан Драпо

98
Вони назвали функцію isEquals??
Себастьян Мах

10
Тут немає потреби в apache-commons-lang - просто використовуйте java.util.Arrays.equals(arr1,arr2)з JDK.
Ед Стауб,

10
@drigoangelo: Я знаю, що знаю. Але мені цікаво, чому вони не просто використовували isEqual, так або, verb adjectiveабо verb noun, замість verb verb. Особисто я просто віддаю перевагу adjectiveлогічним функціям та verbпроцедурам, що змінюють стан.
Себастьян Мах

2
@JonathanDrapeau, ти, мабуть, все-таки матимеш кошмар на технічне обслуговування з таким дизайном ...
MEM Mark

22

Так, ви можете передати масив комутатору. Підвох полягає в тому, що я говорю не про масиви Java, а про структуру даних.

Масив - це систематичне розташування об’єктів, як правило, у рядки та стовпці.

Що ви намагаєтеся зробити, це впровадити систему, яка розпізнає різні прапори, і залежно від увімкнених та вимкнених прапорів ви вживаєте різні дії.

Приклад

Популярною реалізацією такого механізму є дозволи файлів Linux. Де у вас є rwxяк "масив прапорів".

Якщо весь масив є істинним, ви побачите rwx, а це означає, що у вас є всі дозволи. Якщо вам не дозволено виконувати будь-які дії з файлом, увесь масив помилковий, ви побачите ---.

Впровадження

Здогадайтесь, цілі числа ви можете сприймати як масиви. Ціле число представлене "масивом бітів".

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

Ось чому дозвіл rwxможе бути представлений як a7

Фрагмент 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");           
        }                                                                        
    }                                                                            
}

Щоб дізнатись більше про використання бінарного формату, перевірте це запитання: чи можу я в Java визначити цілочисельну константу у двійковому форматі?

Продуктивність

  • Економить пам’ять
  • Вам не потрібно займатися додатковою обробкою, перемиканнями або будь-яким іншим видом жонглювання.

Програми С, які вимагають якомога ефективнішої роботи, використовують цей тип механізму; вони використовують прапори, представлені одинарними бітами.


21

Ні, ви не можете, однак ви можете замінити вищенаведене наступним (визнаю брудним) кодом:

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

3
Я б вклав формулу суми в метод, а не безпосередньо у switchвиписку.
Джонатан Драпо

8
Окрім того, що це помилка помилково, це неправильно: 0010трактується як вісімкове ...
Гіроскоп Gearless

2
Повинен бути метод допоміжного @Alex private;) Також це не гарна ідея, return intоскільки для великих масивів це може переповнюватися.
Марун

1
Мені це подобається, настільки, наскільки здається, що було б простіше підтримувати проти мамонта, якщо / else твердження у прийнятій відповіді
WernerCD

1
Крім того, чому, (values[0]?1:0)*1000якщо ви можете це зробити values[0]?1000:0?
Кранчер

10

Якщо ви намагаєтеся визначити, чи набір умов відповідає дійсності, я замість цього використовую побітові поля.

Наприклад,

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

6

Я обчислюю значення на основі послідовності елементів у логічному масиві, тобто [true, false, true, true]обчислюю до 1011, а потім на основі цього цілого значення ви можете використовувати оператор switch.


Переконайтеся, що якщо ви змінюєте довжину (або порядок) масиву, він все одно працює, якщо ви хочете уникнути переписування кожного оператора 'case'. Якщо Java не дозволяє виклик функції для кожного "випадку", знадобиться масивний ланцюжок if, можливо, з асоціативним масивом для кожного альтернативного випадку та викликом функції для кожного тесту if.
Філ Перрі,

2

Відповідь - НІ. Найкраще пояснення - це навчитися користуватися оператором switch .


Інструкції перемикача беруть лише ints, символи, байти, шорти та типи перечислення. "Правильним" способом зробити це буде або великий блок if..else, або якимось чином відобразити масив булевих значень до цілого числа і діяти відповідно. Може трактувати це як бітову маску?
Девід

1
Комутатори Java також приймають рядки
Вірсавія

2
@Bathsheba Для Java7 +.
Марун

2

Починаючи з JRE 1.7, вам потрібно буде використовувати хакер, я рекомендую:

  • Припустимо values.length <= 64

  • Перетворення значень у longбітлаги, що представляють

  • Switchпроти шістнадцяткових магічних чисел

Злом Java коду:

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

Ця відповідь не Java , а Haxe, оскільки в ній це можливо завдяки збігу шаблонів і має цікавий результат, що може бути корисним для вас, щоб знайти комутатор, який робить те, про що ви просите. Масиви можна підібрати за фіксованою довжиною.

Я створив демо-версію, яка компілюється на Javascript та Flash. Ви можете побачити js-вивід у правому стовпці.

Демо: 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"
  }
}

Це вихідний комутатор, він використовує вкладені комутатори. Якщо ви граєте у справи, ви бачите, як js-ouput змінюється, щоб мати ефективний перемикач.

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

Ще одна цікава модель, яку можна використовувати підкреслення. шаблон _ збігається з чим-небудь, тому case _: дорівнює за замовчуванням, що дозволяє зробити це:

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

Ось ще один підхід, який не вимагає імпорту та бібліотек:

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

Ви також можете подивитися, як Groovy реалізує методи isCase () в Java, використовуйте простішу версію, яка відповідає вашим потребам. Можна помістити це в інтерфейс і створити DSL для порівняння будь-яких двох об’єктів у вашій програмі.

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

Відповідний код висвітлений у рядках 877 - рядках 982


1

@Todor Так, ЦЕ МОЖЛИВО У ЯВІ.

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

Примітка: Я не розробник Java, тому мій синтаксис коду може бути неправильним, але логіка досконала. Ви можете редагувати мою відповідь. Тут я просто спробував перетворити масив у формат рядка, а потім зіставити у випадку перемикача.


0

Я б використовував постійні значення int, які представляють логічний стан.

Якщо ви використовуєте Java 1.7 або вище, ви можете використовувати двійкові літерали, які є більш читабельними .

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

для Java 1.6 та нижче використовуйте будь-які інші літерали int, наприклад hex

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

потім створіть метод, який перетворює логічний масив у цілочисельний бітсет. Напр

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

Нарешті, використовуйте константи у своєму операторі 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;
}

Іншим підходом може бути використання java BitSetта a, Mapщо відображає логіку, яка повинна виконуватися залежно від значення бітсету.

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

і реалізувати свою логіку

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

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.