¿Por qué (i <= j && j <= i && i! = J) se evalúa como VERDADERO?

104

He escrito un fragmento de código Java que se ejecuta en un bucle infinito.

A continuación se muestra el código:

public class TestProgram {
    public static void main(String[] args){
        Integer i = new Integer(0);
        Integer j = new Integer(0);

        while(i<=j && j<=i && i!=j){
            System.out.println(i);
        }
    }
}

En el código anterior, mientras ve la condición en el whileciclo, al principio parece que ese programa no entrará en el whileciclo. Pero en realidad es un ciclo infinito y sigue imprimiendo el valor.

¿Que está sucediendo aquí?

Kshitij Jain
fuente
8
La respuesta simple es que i<=j && j<=i && i!=jesta condición siempre se evalúa como verdadera. Solo toma un pedazo de papel y evalúa que lo atraparás :)
Pradeep Simha
4
La forma en que está creando el entero es incorrecta. Use 'compareTo'
nachokk
7
Si nunca cambia io j, ¿cuándo esperaría que terminara el ciclo?
Fred Larson
33
@PradeepSimha Para valores int simples, esto siempre daría como resultado falso . De i<=jy j<=ise puede concluir, eso i == j, que contradice el último término. Por lo tanto, toda la expresión se evalúa como falsa y no se ingresaría el while. ¡El punto clave es la identidad del objeto aquí!
Sirko
4
Aparte, este es el acertijo 32 del libro Java Puzzlers: Traps, Pitfalls, and Corner Cases.
Cyanfish

Respuestas:

188
  • i <= jse evalúa true, porque el unboxing automático ocurre para las comparaciones int y luego tanto iyj mantenga el valor predeterminado, 0.

  • j <= i es evaluado para true debido a la razón anterior.

  • i != jse evalúa a true, porque tantoi y json objetos diferentes. Y al comparar objetos, no es necesario un desempaquetado automático.

Todas las condiciones son verdaderas y no está cambiando ini jen bucle, por lo que se ejecuta infinitamente.

Juned Ahsan
fuente
10
¿Puede explicar por qué! = está comprobando el índice de memoria de los objetos de referencia y <= está comprobando el valor sin recuadrar de Integer ?? .. ¿Por qué hay tanta diferencia entre estos operadores?
Punith Raj
41
Los operadores @PunithRaj <&> funcionan en primitivas y no en objetos, por lo tanto, el desempaquetado automático ocurre para estos operadores. Pero los operadores == y! = También se pueden usar para la comparación de objetos, por lo que no es necesario desempaquetar aquí, por lo tanto, los objetos se comparan.
Juned Ahsan
14
¡¡Ah, los peligros ocultos del boxeo / unboxing implícito !!
Hot Licks
3
Stack Overflow solo debería agregar una nueva etiqueta, "El desempaquetado automático fue el mayor error jamás cometido en Java". :-). A excepción de los escritores de los libros Java Puzzler. Úselo para etiquetar preguntas como estas.
user949300
4
tenga en cuenta que Integer.valueOf(0) == Integer.valueOf(0)siempre se evalúa como verdadero porque en este caso se devuelve el mismo objeto (consulte IntegerCache grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… )
Vitalii Fedorenko
40

Porque estas comparando

  • 0 < = 0 (true) // unboxing

  • 0 > = 0 (true) // unboxing

  • reference != secondReference (true)mientras crea objetos, no una comparación primitiva. Entonces evalúa a while(true) { // Never ending loop }.

Ashwani
fuente
2
¡Oh! Dragón oculto de auto UNBOXING ... Buena Explicación.
HybrisHelp
17

Los objetos enteros son diferentes. Es diferente del tipo int básico.

Vea esta respuesta: ¿Cómo comparar correctamente dos enteros en Java?

La i != jparte es verdadera, que esperabas que fuera falsa.

Coronel Panic
fuente
Si bien es cierto, no importa aquí ni responde a la pregunta.
Kon
6
@Kon: De hecho, esta es la respuesta. Las condiciones # 1 y # 2 se evalúan truedebido al autoboxing. En el caso del # 3, el autoboxing no se aplica y la comparación se realiza a nivel de objeto (ubicación de memoria).
casa
1

El ciclo no termina porque su condición es verdadera (i! = J es verdadera porque hay 2 objetos diferentes, use Integer.valueOf en su lugar) y dentro del ciclo los valores no cambian, por lo que su condición permanece verdadera para siempre.

Silviu Burcea
fuente
1

Los objetos enteros son diferentes. Es diferente del tipo int básico. para que puedas hacer eso. lo que haces es simplemente comparar el objeto y, por supuesto, el resultado es verdadero.

Krichevskoy
fuente
1

Hay dos casos diferentes que tenemos que entender primero,

caso 1:

        Integer i = new Integer(10);
        Integer j = new Integer(10);

        System.out.println((i<=j && j<=i && i!=j));
        System.out.println(i!=j);

caso 2:

        Integer i = 10;
        Integer j = 10;

        System.out.println((i<=j && j<=i && i==j));
        System.out.println(i==j);

ambos son diferentes, como

en el caso 1: i!=jserá trueporque ambos hacen referencia a dos objetos diferentes en el montón y no pueden ser iguales. Pero

en el caso 2: i==jserá trueporque ambos 10 son literales enteros y Java mantiene pool for Integer literalscuál tiene valor (-128 <= X <= 127). Entonces, en este caso 10 <= 127 resulta verdadero, por lo que ambos tendrán referencia al mismo objeto.

Akhilesh Dhar Dubey
fuente
0

Quizás la razón es que tanto 'i' como 'j' son objetos, y la comparación de objetos no es lo mismo que la comparación de referencias de objetos. Considere usar! I.equals (j) en lugar de i! = J

Eric Gopak
fuente
0

El programa sigue mostrando el mismo valor de iporque no está incrementando ni disminuyendo el valor de io j. La condición en for siempre sigue evaluándose como verdadera, por lo que es un bucle infinito.

user28646
fuente
Creo que la pregunta era más sobre la i!=jparte que sorprendentemente se evalúa como verdadera y no las <=comparaciones.
Soravux
0

Entero a = nuevo Entero (0); Entero b = nuevo Entero (0);

Las comparaciones <= y> = usarán el valor 0 sin caja, mientras que! = Comparará las referencias y tendrá éxito ya que son objetos diferentes.

Incluso esto también funciona i, e

Entero a = 1000; Entero b = 1000;

pero esto no:

Entero a = 100; Entero b = 100;

La razón es que Integer utiliza internamente el almacenamiento en caché para objetos Integer entre -128 y 127 y devuelve instancias de esa caché para el rango que cubre. No estoy seguro, pero supongo que también puede cambiar su valor máximo en el paquete "java.lang.Integer.IntegerCache.high".

Para una mejor comprensión, consulte la URL: https://www.owasp.org/index.php/Java_gotchas#Immutable_Objects_.2F_Wrapper_Class_Caching

Waheed
fuente
-3

debe saber que es un poco diferente en && this y this & cuando usa && luego, cuando la primera condición es verdadera, entonces verifica la segunda condición si es falsa, entonces no verificó la tercera condición porque en el operador & si una condición es falsa todos los declaración es falsa si usa || luego, si ve verdadero, devuelve verdadero en su código porque i y j son iguales, la primera y la segunda condición son verdaderas, luego en la tercera condición será falsa porque son iguales y la condición while es falsa.

Sara Sodagari
fuente
No sé por qué mi respuesta obtiene valor de minas porque mi respuesta es verdadera, vea este enlace, es cierto, entonces, antes de obtener minas para mi respuesta, lea más stackoverflow.com/questions/5564410/difference-between-and
sara Sodagari