67

Tengo un largo conjunto de comparaciones que hacer en Java, y me gustaría saber si una o más de ellas aparecen como verdaderas. La secuencia de comparaciones fue larga y difícil de leer, así que la descompuse para poder leerla y automáticamente fui a usar un operador de acceso directo |= en lugar de negativeValue = negativeValue || boolean.Operador de acceso directo "or-assignment" (| =) en Java

boolean negativeValue = false; 
negativeValue |= (defaultStock < 0); 
negativeValue |= (defaultWholesale < 0); 
negativeValue |= (defaultRetail < 0); 
negativeValue |= (defaultDelivery < 0); 

espero negativeValue para ser verdad si alguna de las predeterminado <algo> valores son negativos. Es esto valido? ¿Hará lo que espero? No pude verlo mencionado en el sitio de Sun o stackoverflow, pero Eclipse no parece tener un problema y el código se compila y ejecuta.


Del mismo modo, si quería realizar varias intersecciones lógicas, podría utilizar en lugar de &=&&?

+13

¿Por qué no lo intentas? – Roman

+0

Esta es una lógica booleana general, no solo Java. para que pueda buscarlo en otros lugares. ¿Y por qué no lo intentas? – Dykam

+14

@Dykam: No, hay un comportamiento específico aquí. Java * podría * elegir hacer | = un cortocircuito, de modo que no evaluará el RHS si el LHS ya es verdadero, pero no es así. –

Respuesta

171

El |= es un operador de asignación compuesto (JLS 15.26.2) para el operador lógico booleano | (JLS 15.22.2); no debe confundirse con el condicional o || (JLS 15.24). También hay &= y ^= correspondientes a la versión de asignación compuesta del booleano lógico & y ^, respectivamente.

En otras palabras, para boolean b1, b2, estos dos son equivalentes:

b1 |= b2; 
b1 = b1 | b2; 

La diferencia entre los operadores lógicos (& y |) en comparación con sus homólogos condicionales (&& y ||) es que el primero no lo hacen "cortocircuito"; este último lo hace. Es decir:

  • & y | siempre evalúan ambos operandos
  • && y || evaluar el operando derecho condicionalmente; el operando correcto se evalúa solo si su valor podría afectar el resultado de la operación binaria.Eso significa que el operando de la derecha no se evalúa cuando:
    • El operando izquierdo de && evalúa a false
      • (porque no importa lo que el operando de la derecha se evalúa como, toda la expresión es false)
    • El operando de la izquierda de || evalúa a true
      • (porque no m atter lo que el operando de la derecha se evalúa como, toda la expresión es true)

Así que volviendo a su pregunta original, sí, esa construcción es válida, y mientras |= no es exactamente un acceso directo equivalente para = y ||, calcula lo que desea. Dado que el lado derecho del operador |= en su uso es una operación de comparación entera simple, el hecho de que | no se cortocircuite es insignificante.

Hay casos, cuando se desea un cortocircuito, o incluso se requiere, pero el escenario no es uno de ellos.

Desafortunadamente, a diferencia de otros lenguajes, Java no tiene &&= y ||=. Esto fue discutido en la pregunta Why doesn't Java have compound assignment versions of the conditional-and and conditional-or operators? (&&=, ||=).

+0

+1, muy completo. parece plausible que un compilador bien podría convertirse en un operador de cortocircuito, si pudiera determinar que el RHS no tiene efectos secundarios. alguna pista sobre eso? – Carl

+0

Leí que cuando RHS es trivial y SC no es necesario, los operadores SC "inteligentes" son en realidad un poco más lentos. Si es cierto, entonces es más interesante preguntarse si algunos compiladores pueden convertir SC a NSC en determinadas circunstancias. – polygenelubricants

+0

@polygenelubricants cortocircuitado operadores implican una rama de algún tipo bajo el capó, por lo que si no hay patrón de predicción de bifurcación de los valores de verdad utilizados con los operadores y/o la arquitectura en uso no tiene buena/cualquier predicción de bifurcación (y suponiendo que el compilador y/o la máquina virtual no realizan ninguna optimización relacionada por sí mismos), entonces sí, los operadores de SC presentarán cierta lentitud en comparación con el no cortocircuito. La mejor forma de descubrir algo del compilador sería compilar un programa usando SC vs. NSC y luego comparar el bytecode para ver si difiere. – JAB

15

No es un operador de "atajo" (o cortocircuito) en la forma en que || y & & (ya que no evaluarán el RHS si ya conocen el resultado basado en el LHS) pero hará lo que usted desee en términos de trabajando.

Como un ejemplo de la diferencia, este código va a estar bien si text es nulo:

boolean nullOrEmpty = text == null || text.equals("") 

mientras que esto no será:

boolean nullOrEmpty = false; 
nullOrEmpty |= text == null; 
nullOrEmpty |= text.equals(""); // Throws exception if text is null 

(Obviamente se podría hacer para que "".equals(text) particular, caso - Estoy tratando de demostrar el principio.)

0
List<Integer> params = Arrays.asList (defaultStock, defaultWholesale, 
             defaultRetail, defaultDelivery); 
int minParam = Collections.min (params); 
negativeValue = minParam < 0; 
+0

Creo que preferiría 'negativeValue = defaultStock <0 || defaultWholesale <0' etc. Aparte de la ineficacia de todo el boxeo y la envoltura que están sucediendo aquí, no me parece tan fácil de entender lo que su código realmente significaría. –

+0

I tiene incluso más de 4 parámetros y el criterio es el mismo para todos ellos, entonces me gusta mi solución, pero en aras de la legibilidad, la separaría en varias líneas (es decir, crear lista, encontrar valor mínimo, comparar valor mínimo con 0) – Roman

+0

Eso parece útil para una gran cantidad de comparaciones. En este caso, creo que la reducción de la claridad no justifica el ahorro en mecanografía, etc. –

1

Puede tener solo una declaración. Expresado en varias líneas se lee casi exactamente igual que el código de ejemplo, sólo es menos imperativa:

boolean negativeValue 
    = defaultStock < 0 
    | defaultWholesale < 0 
    | defaultRetail < 0 
    | defaultDelivery < 0; 

Para las expresiones más simples, utilizando | puede ser más rápido que || porque aunque evita hacer una comparación que significa el uso de una rama implícita y eso puede ser mucho más caro.

+0

Empecé con solo uno, pero como dije en la pregunta original, sentí que "La cadena de comparaciones era larga y difícil de leer, así que lo rompí para la legibilidad ". Aparte de eso, en este caso estoy más interesado en aprender el comportamiento de | = que en hacer que este código en particular funcione. –

1

Aunque puede ser demasiado para su problema, la biblioteca Guava tiene una buena sintaxis con Predicate sy realiza una evaluación de cortocircuito de 0/Predicate s.

Esencialmente, las comparaciones se convierten en objetos, se empaquetan en una colección y luego se repiten. Para o predicados, el primer hit verdadero regresa de la iteración, y viceversa para y.

0

|| lógico booleano O
| OR bit a bit

| = bit a bit OR inclusivo y asignación operador

La razón por | = no shortcircit es porque hace un OR no un OR lógico. Es decir:

 

C |= 2 is same as C = C | 2 

Tutorial for java operators

+0

NO hay bits O con booleanos! El operador '|' es entero en el modo OR ** pero también es ** lógico O - ver [Especificación del lenguaje Java 15.22.2.] (Http://docs.oracle.com/javase/specs/jls/se7/html/ jls-15.html # jls-15.22.2 "JLS 15.22.2") –

+0

Tiene razón porque para un solo bit (booleano) en modo bit lógico y OR es equivalente. Aunque prácticamente, el resultado es el mismo. – oneklc

1

Si se trata de legibilidad Tengo el concepto de separación de datos a prueba de la lógica de la prueba.Ejemplo de código:

// declare data 
DataType [] dataToTest = new DataType[] { 
    defaultStock, 
    defaultWholesale, 
    defaultRetail, 
    defaultDelivery 
} 

// define logic 
boolean checkIfAnyNegative(DataType [] data) { 
    boolean negativeValue = false; 
    int i = 0; 
    while (!negativeValue && i < data.length) { 
     negativeValue = data[i++] < 0; 
    } 
    return negativeValue; 
} 

El código parece más prolijo y autoexplicativo. Usted puede incluso crear una matriz en la llamada al método, como esto:

checkIfAnyNegative(new DataType[] { 
    defaultStock, 
    defaultWholesale, 
    defaultRetail, 
    defaultDelivery 
}); 

Es más fácil de leer que 'cadena de comparación', y también tiene la ventaja de rendimiento de cortocircuito (a costa de asignación de matriz y llamada al método).

Editar: legibilidad Aún más puede ser alcanzado simplemente usando varargs parámetros:

firma método sería:

boolean checkIfAnyNegative(DataType ... data) 

Y la llamada podría tener este aspecto:

checkIfAnyNegative(defaultStock, defaultWholesale, defaultRetail, defaultDelivery); 
+1

La asignación de matrices y una llamada a un método son un costo bastante grande para los cortocircuitos, a menos que tenga operaciones costosas en las comparaciones (sin embargo, el ejemplo en la pregunta es barato). Habiendo dicho eso, la mayor parte del tiempo la mantenibilidad del código prevalecerá sobre las consideraciones de rendimiento. Probablemente usaría algo así si estuviera haciendo la comparación de manera diferente en un montón de lugares diferentes o comparando más de 4 valores, pero para un solo caso es algo detallado para mi gusto. –

+0

@DavidMason Estoy de acuerdo. Sin embargo, tenga en cuenta que la mayoría de las calculadoras modernas se tragarían ese tipo de sobrecarga en menos de unos pocos milis. Personalmente, no me interesaría la sobrecarga hasta los problemas de rendimiento, que [parece ser razonable] (http://en.wikipedia.org/wiki/Program_optimization#When_to_optimize). Además, la verbosidad del código es una ventaja, especialmente cuando javadoc no es proporcionado o generado por JAutodoc. –