Por que (i <= j && j <= i && i! = J) é avaliado como VERDADEIRO?

104

Escrevi um trecho de código Java que está sendo executado em um loop infinito.

Abaixo está o 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);
        }
    }
}

No código acima, ao ver a condição no whileloop, a princípio parece que o programa não entrará no whileloop. Mas na verdade é um loop infinito e continua imprimindo o valor.

O que esta acontecendo aqui?

Kshitij Jain
fonte
8
A resposta simples é que i<=j && j<=i && i!=jessa condição sempre é avaliada como verdadeira. Pegue um pedaço de papel e avalie que você vai pegá-lo :)
Pradeep Simha
4
A maneira como você está criando o inteiro está incorreta. Use 'compareTo'
nachokk
7
Se você nunca mudar iou j, quando espera que o loop termine?
Fred Larson,
33
@PradeepSimha Para valores int simples, isso sempre resultaria em falso . De i<=je j<=ivocê pode concluir, isso i == j, que contradiz o último termo. Portanto, toda a expressão é avaliada como falsa e o while não seria inserido. O ponto chave é a identidade do objeto aqui!
Sirko
4
À parte, este é o quebra-cabeça 32 do livro Java Puzzlers: Traps, Pitfalls and Corner Cases.
Cyanfish

Respostas:

188
  • i <= jé avaliado como true, porque o unboxing automático acontece para comparações internas e, em seguida, ie jmantém o valor padrão 0,.

  • j <= ié avaliado truedevido ao motivo acima.

  • i != jé avaliado como true, porque ie jsão objetos diferentes. E ao comparar objetos, não há necessidade de desempacotamento automático.

Todas as condições são verdadeiras e você não está mudando ie jem loop, portanto, ele está funcionando infinitamente.

Juned Ahsan
fonte
10
você pode explicar, por que! = está verificando o índice de memória dos objetos de referência e <= está verificando o valor não encaixotado de Inteiro ?? .. por que há tal diferença entre esses operadores?
Punith Raj
41
Os operadores @PunithRaj <&> funcionam em primitivas e não em objetos, portanto, o desempacotamento automático acontece para esses operadores. Mas os operadores == e! = Também podem ser usados ​​para comparação de objetos, portanto, não há necessidade de desempacotar aqui, portanto, os objetos são comparados.
Juned Ahsan
14
Ah, os perigos ocultos do boxe / desencaixotamento implícito !!
Hot Licks
3
O Stack Overflow deve apenas adicionar uma nova tag, "Desembalagem automática foi o maior erro já cometido em Java". :-). Exceto para escritores dos livros Java Puzzler. Use-o para marcar perguntas como essas.
user949300,
4
observe que Integer.valueOf(0) == Integer.valueOf(0)é sempre avaliado como verdadeiro porque, neste caso, o mesmo objeto é retornado (consulte IntegerCache grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… )
Vitalii Fedorenko
40

Porque você está comparando

  • 0 < = 0 (true) // unboxing

  • 0 > = 0 (true) // unboxing

  • reference != secondReference (true)como você está criando objetos, não uma comparação primitiva. Portanto, é avaliado como while(true) { // Never ending loop }.

Ashwani
fonte
2
Ohh! Dragão escondido de auto DESBOXAR ... Boa explicação.
HybrisHelp
17

Os objetos inteiros são diferentes. É diferente do tipo básico interno.

Veja esta resposta: Como comparar corretamente dois inteiros em Java?

A i != jparte é verdadeira, que você esperava que fosse falsa.

Coronel Panic
fonte
Embora seja verdade, não importa aqui nem responde à pergunta.
Kon
6
@Kon: Na verdade, esta é a resposta. As condições nº 1 e nº 2 avaliam por truecausa do autoboxing. No caso de # 3, o autoboxing não se aplica e a comparação ocorre no nível do objeto (localização da memória).
casa em
1

O loop não está terminando porque sua condição é verdadeira (i! = J é verdadeira porque existem 2 objetos diferentes, use Integer.valueOf em vez disso) e dentro do loop os valores não estão mudando, então sua condição permanece verdadeira para sempre.

Silviu Burcea
fonte
1

Os objetos inteiros são diferentes. É diferente do tipo básico interno. então você pode simplesmente fazer assim. o que você faz é apenas comparar o objeto e, claro, o resultado é verdadeiro.

Krichevskoy
fonte
1

Existem dois casos diferentes que devemos entender primeiro,

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 são diferentes, como

no caso 1: i!=jserá trueporque ambos fazem referência a dois objetos diferentes no heap e não podem ser iguais. Mas

no caso 2: i==jserá trueporque ambos 10 são literais inteiros e Java mantém pool for Integer literalsque têm valor (-128 <= X <= 127). Portanto, neste caso 10 <= 127 resultados verdadeiros, então ambos terão referência ao mesmo objeto.

Akhilesh Dhar Dubey
fonte
0

Talvez a razão seja que 'i' e 'j' são objetos, e comparação de objetos não é o mesmo que comparação de referência de objetos. Considere usar! I.equals (j) em vez de i! = J

Eric Gopak
fonte
0

O programa continua exibindo o mesmo valor de iporque você não está incrementando ou diminuindo o valor de iou j. A condição em for sempre continua avaliando como verdadeira, portanto, é um loop infinito.

user28646
fonte
Acho que a questão era mais sobre a i!=jparte que surpreendentemente avalia como verdadeira e não sobre as <=comparações.
Soravux de
0

Inteiro a = novo Inteiro (0); Inteiro b = novo Inteiro (0);

As comparações <= e> = usarão o valor não encaixotado 0, enquanto o! = Comparará as referências e terá êxito, pois são objetos diferentes.

Mesmo isso também funcionará i, e

Inteiro a = 1000; Inteiro b = 1000;

mas isso não:

Inteiro a = 100; Inteiro b = 100;

O motivo é porque Integer usa internamente o cache para objetos Integer entre -128 e 127 e retorna instâncias desse cache para o intervalo que cobre. Não tenho certeza, mas acho que você também pode alterar seu valor máximo no pacote "java.lang.Integer.IntegerCache.high".

Para melhor compreensão, verifique o url: https://www.owasp.org/index.php/Java_gotchas#Immutable_Objects_.2F_Wrapper_Class_Caching

Waheed
fonte
-3

você tem que saber que é um pouco diferente em && isto e isto & quando você usa && então quando a primeira condição é verdadeira então ele verifica a segunda condição se for falsa então não verificou a terceira condição porque no operador & se uma condição for falsa todas as declaração é falsa se usar || então, se for verdadeiro, então ele retornará verdadeiro em seu código porque i e j são iguais primeiro e a segunda condição é verdadeira, então na terceira condição será falso porque eles são iguais e enquanto a condição é falsa.

sara Sodagari
fonte
não sei por que minha resposta obtém valor de minas porque minha resposta é verdadeira veja este link é verdade, então antes de obter minas para minha resposta, leia mais stackoverflow.com/questions/5564410/difference-between-and
sara Sodagari