Использовать массив в качестве аргумента case в переключателе
Я пытаюсь сделать что-то вроде этого, т.е. использовать массив в инструкции 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;
}
Ответы
Ответ 1
NO, просто вы не можете.
SwitchStatement:
switch ( Expression ) SwitchBlock
Тип выражения должен быть char, byte, short, int, Character, Byte, Short, Integer, String или тип перечисления (§8.9), или возникает ошибка времени компиляции.
http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.11
Ответ 2
@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;
}
Ответ 3
Вы не можете включать целые массивы. Но вы можете преобразовать в бит, установленный за счет некоторой читаемости самого switch
:
switch (values[0] + 2 * values[1] + 4 * values[2] + 8 * values[3])
и использовать бинарные литералы в ваших случаях: case 0b0101
- ваш первый.
Ответ 4
Попробуйте это решение:
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 {
...
}
Смотрите docs здесь.
Ответ 5
Да, вы можете передать массив коммутатору. Уловка в том, что я не говорю о массивах Java, а о структуре данных.
Массив - это систематическое расположение объектов, обычно в строках и столбцах.
То, что вы пытаетесь сделать, - это реализовать систему, которая распознает разные флаги и в зависимости от флагов, которые включены или выключены, вы выполняете разные действия.
Пример
Популярной реализацией такого механизма является доступ к файлам Linux. Где у вас rwx
как "массив флагов".
Если весь массив равен true, вы увидите 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
может быть представлено как 7
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 можно определить целочисленную константу в двоичном формате?
Производительность
- Сохраняет память
- Вам не нужно выполнять дополнительную обработку, переключение или любой другой тип жонглирования.
C программы, которые требуют как можно более эффективного использования этого типа механизма; они используют флаги, представленные единичными битами.
Ответ 6
Нет, вы не можете, однако вы можете заменить вышеуказанный код (грязный, я допускаю):
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);
}
Ответ 7
Если вы пытаетесь определить, является ли набор условий истинным, вместо этого я использовал бы побитовые поля.
Например,
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 ;
}
}
}
Ответ 8
Я бы вычислил значение, основанное на последовательности элементов в булевом массиве, т.е. [true, false, true, true]
будет оцениваться до 1011, а затем на основе этого целочисленного значения вы можете использовать оператор switch.
Ответ 9
Ответ НЕТ. Лучшее объяснение - узнать, как использовать оператор switch.
Ответ 10
Как и в JRE 1.7, вам нужно будет использовать хак, я рекомендую:
-
Предположим values.length <= 64
-
Преобразовать значения в long
, представляющие битфлаги
-
Switch
против шестнадцатеричных магических чисел
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;
}
Ответ 11
Вот еще один подход, который не требует импорта и библиотек:
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);
}
Ответ 12
Этот ответ - не Java, а Haxe, потому что это возможно в результате сопоставления шаблонов и имеет интересный вывод, который может быть полезен для вас, чтобы найти переключатель, который делает то, о чем вы просите. Массивы могут быть сопоставлены по фиксированной длине.
Я создал демоверсию, которая компилируется в Javascript и Flash. Вы можете увидеть js-вывод в правом столбце.
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"
}
}
Это выведенный коммутатор, он использует вложенные ключи. Если вы играете с этими случаями, вы увидите, как изменяется 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();
})();
Еще одна интересная модель, в которой вы можете использовать символы подчеркивания. a_ pattern соответствует чему угодно, поэтому 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
Ответ 13
Вы также можете посмотреть, как Groovy реализует методы isCase() в Java, используйте более простую версию, соответствующую вашим потребностям. Можно поместить это в интерфейс и создать DSL для сравнения любых двух объектов в вашем приложении.
return isCase(DefaultTypeTransformation.asCollection(caseValue), switchValue);
Соответствующий код описан в Линии 877 через Строки 982
Ответ 14
@Todor Да, ЭТО ВОЗМОЖНО В 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;
}
Примечание. Я не разработчик Java, поэтому мой синтаксис кода может быть неправильным, но логика идеальна. Вы можете отредактировать мой ответ. Здесь я просто попытался преобразовать массив в строковый формат и затем совместить в случае коммутатора.
Ответ 15
Я бы использовал постоянные значения 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
и 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";
}
}