ทำไม (i <= j && j <= i && i! = j) จึงประเมินค่าเป็น TRUE


105

ฉันได้เขียนโค้ด Java ชิ้นหนึ่งซึ่งทำงานในลูปที่ไม่มีที่สิ้นสุด

ด้านล่างนี้คือรหัส:

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

ในโค้ดด้านบนขณะที่เห็นเงื่อนไขในwhileลูปตอนแรกดูเหมือนว่าโปรแกรมจะไม่เข้าไปในwhileลูป แต่จริงๆแล้วมันเป็นวงวนที่ไม่มีที่สิ้นสุดและพิมพ์ค่าไปเรื่อย ๆ

เกิดอะไรขึ้นที่นี่?


8
คำตอบง่ายๆคือi<=j && j<=i && i!=jเงื่อนไขนี้จะประเมินเป็นจริงเสมอ แค่เอากระดาษมาประเมินก็จะจับได้ :)
Pradeep Simha

4
วิธีที่คุณสร้างจำนวนเต็มไม่ถูกต้อง ใช้ '
CompareTo

7
ถ้าคุณไม่เคยเปลี่ยนiหรือjคาดว่าลูปจะยุติเมื่อใด
Fred Larson

33
@PradeepSimha สำหรับค่า int อย่างง่ายสิ่งนี้จะให้ผลเป็นเท็จเสมอ จากi<=jและj<=iคุณสามารถสรุปได้ว่าi == jซึ่งขัดแย้งกับเทอมสุดท้าย ดังนั้นนิพจน์ทั้งหมดจึงประเมินเป็นเท็จและจะไม่ป้อน while ประเด็นสำคัญคือเอกลักษณ์ของวัตถุที่นี่!
Sirko

4
นอกจากนี้ยังเป็นปริศนา 32 ในหนังสือ Java Puzzlers: กับดักหลุมพรางและกรณีมุม
Cyanfish

คำตอบ:


189
  • i <= jได้รับการประเมินไปtrueเพราะแกะกล่องอัตโนมัติที่เกิดขึ้นสำหรับการเปรียบเทียบ int แล้วทั้งสองiและเก็บค่าเริ่มต้นj0

  • j <= iได้รับการประเมินtrueเนื่องจากเหตุผลข้างต้น

  • i != jได้รับการประเมินเป็นtrueเพราะทั้งสองอย่างiและjเป็นวัตถุที่แตกต่างกัน และในขณะที่เปรียบเทียบวัตถุก็ไม่จำเป็นต้องแกะกล่องอัตโนมัติ

เงื่อนไขทั้งหมดเป็นจริงและคุณไม่ได้เปลี่ยนiและjวนซ้ำดังนั้นมันจึงทำงานไม่สิ้นสุด


10
คุณช่วยอธิบายได้ไหมทำไม! = กำลังตรวจสอบดัชนีหน่วยความจำของวัตถุอ้างอิงและ <= กำลังตรวจสอบค่าที่ไม่ใส่กล่องของจำนวนเต็ม ?? .. ทำไมจึงมีความแตกต่างระหว่างตัวดำเนินการเหล่านี้
Punith Raj

41
ตัวดำเนินการ @PunithRaj <&> ทำงานบนพื้นฐานไม่ใช่วัตถุดังนั้นการแกะกล่องอัตโนมัติจึงเกิดขึ้นสำหรับตัวดำเนินการเหล่านี้ แต่ == และ! = ตัวดำเนินการสามารถใช้สำหรับการเปรียบเทียบวัตถุได้เช่นกันดังนั้นจึงไม่จำเป็นต้องแกะกล่องที่นี่ดังนั้นจึงเปรียบเทียบวัตถุ
Juned Ahsan

14
อา.. อันตรายที่ซ่อนอยู่ของนัยหมวย / แกะกล่อง !!
Hot Licks

3
Stack Overflow ควรเพิ่มแท็กใหม่ "การแกะกล่องอัตโนมัติเป็นข้อผิดพลาดที่ใหญ่ที่สุดที่เคยเกิดขึ้นใน Java" :-). ยกเว้นผู้เขียนหนังสือ Java Puzzler ใช้แท็กคำถามเหล่านี้
user949300

4
หมายเหตุที่Integer.valueOf(0) == Integer.valueOf(0)ประเมินเป็นจริงเสมอเนื่องจากในกรณีนี้จะส่งคืนวัตถุเดียวกัน (ดู IntegerCache grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… )
Vitalii Fedorenko

41

เพราะคุณกำลังเปรียบเทียบ

  • 0 < = 0 (true) // unboxing

  • 0 > = 0 (true) // unboxing

  • reference != secondReference (true)ในขณะที่คุณกำลังสร้างวัตถุไม่ใช่การเปรียบเทียบแบบดั้งเดิม while(true) { // Never ending loop }ดังนั้นจึงประเมิน


2
โอ้โห! มังกรที่ซ่อนอยู่ของการปลดกล่องอัตโนมัติ ... คำอธิบายที่ดี
HybrisHelp

17

วัตถุจำนวนเต็มแตกต่างกัน มันแตกต่างจากประเภท int พื้นฐาน

ดูคำตอบนี้: จะเปรียบเทียบสองจำนวนเต็มใน Java ได้อย่างไร?

i != jส่วนหนึ่งเป็นความจริงที่คุณถูกคาดหวังว่าจะเป็นเท็จ


แม้ว่าจะเป็นความจริง แต่ก็ไม่สำคัญที่นี่หรือไม่ตอบคำถาม
อน

6
@Kon: อันที่จริงนี่คือคำตอบ เงื่อนไข # 1 และ # 2 ประเมินว่าเป็นtrueเพราะ autoboxing ในกรณีของ # 3 autoboxing ใช้ไม่ได้และการเปรียบเทียบจะเกิดขึ้นในระดับวัตถุ (ตำแหน่งหน่วยความจำ)
บ้าน

2

มีสองกรณีที่แตกต่างกันซึ่งเราต้องทำความเข้าใจก่อน

กรณีที่ 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);

กรณีที่ 2:

        Integer i = 10;
        Integer j = 10;

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

ทั้งสองต่างกันเช่น

ในกรณีที่ 1: i!=jจะเป็นtrueเพราะทั้งสองอ้างอิงไปยังวัตถุที่แตกต่างกันสองชิ้นในฮีปและไม่สามารถเหมือนกันได้ แต่

ในกรณีที่ 2: i==jจะเป็นtrueเพราะทั้งสองเป็นจำนวนเต็ม 10 ตัวอักษรและ Java รักษาซึ่งมีมูลค่าpool for Integer literals (-128 <= X <= 127)ดังนั้นในกรณีนี้ 10 <= 127 ผลลัพธ์เป็นจริงดังนั้นทั้งสองจะมีการอ้างอิงถึงวัตถุเดียวกัน


1

ลูปไม่สิ้นสุดเนื่องจากเงื่อนไขของคุณเป็นจริง (i! = j เป็นจริงเนื่องจากมี 2 อ็อบเจ็กต์ที่แตกต่างกันให้ใช้ Integer.valueOf แทน) และภายในลูปค่าจะไม่เปลี่ยนแปลงดังนั้นเงื่อนไขของคุณจึงยังคงเป็นจริงตลอดไป


1

บางทีเหตุผลก็คือทั้ง 'i' และ 'j' เป็นวัตถุและการเปรียบเทียบวัตถุไม่เหมือนกับการเปรียบเทียบการอ้างอิงวัตถุ โปรดพิจารณาใช้! i.equals (j) แทน i! = j


1

วัตถุจำนวนเต็มแตกต่างกัน มันแตกต่างจากประเภท int พื้นฐาน คุณก็ทำแบบนั้นได้ สิ่งที่คุณทำมันเป็นเพียงการเปรียบเทียบวัตถุและแน่นอนว่าผลลัพธ์นั้นเป็นจริง


1

จำนวนเต็ม a = จำนวนเต็มใหม่ (0); จำนวนเต็ม b = จำนวนเต็มใหม่ (0);

การเปรียบเทียบ <= และ> = จะใช้ค่าที่ไม่มีกล่องเป็น 0 ในขณะที่! = จะเปรียบเทียบการอ้างอิงและจะประสบความสำเร็จเนื่องจากเป็นวัตถุที่แตกต่างกัน

แม้สิ่งนี้จะใช้งานได้เช่นกัน i, e

จำนวนเต็ม a = 1,000; จำนวนเต็ม b = 1000;

แต่สิ่งนี้ไม่:

จำนวนเต็ม a = 100; จำนวนเต็ม b = 100;

สาเหตุเป็นเพราะ Integer ใช้การแคชภายในสำหรับอ็อบเจ็กต์ Integer ระหว่าง -128 ถึง 127 และส่งคืนอินสแตนซ์จากแคชนั้นสำหรับช่วงที่ครอบคลุม ฉันไม่แน่ใจ แต่ฉันเดาว่าคุณสามารถเปลี่ยนค่าสูงสุดในแพ็คเกจ "java.lang.Integer.IntegerCache.high" ได้

เพื่อความเข้าใจที่ดีขึ้นตรวจสอบ url: https://www.owasp.org/index.php/Java_gotchas#Immutable_Objects_.2F_Wrapper_Class_Caching


0

โปรแกรมที่ช่วยในการแสดงค่าเดียวกันของiเพราะคุณไม่ได้ที่เพิ่มขึ้นหรือ decrementing ทั้งค่าของหรือi jเงื่อนไขใน for มักจะประเมินเป็นจริงเสมอดังนั้นจึงเป็นวงวนที่ไม่มีที่สิ้นสุด


ฉันคิดว่าคำถามนั้นเกี่ยวกับi!=jส่วนที่ประเมินได้อย่างน่าประหลาดใจว่าเป็นจริงไม่ใช่การ<=เปรียบเทียบ
Soravux

-3

คุณต้องรู้ว่ามันแตกต่างกันเล็กน้อยใน && this และสิ่งนี้ & เมื่อคุณใช้ && เมื่อเงื่อนไขแรกเป็นจริงให้ตรวจสอบเงื่อนไขที่สองว่าเป็นเท็จหรือไม่ก็ไม่ได้ตรวจสอบเงื่อนไขที่สามเพราะใน & ตัวดำเนินการหากเงื่อนไขหนึ่งเป็นเท็จทั้งหมด คำสั่งเป็นเท็จถ้าใช้ || ถ้ามันเห็นเป็นจริงมันจะคืนค่าจริงในโค้ดของคุณเพราะ i และ j เท่ากันเงื่อนไขแรกและเงื่อนไขที่สองเป็นจริงแล้วในเงื่อนไขที่สามมันจะเป็นเท็จเพราะมันเท่ากันและในขณะที่เงื่อนไขเป็นเท็จ


ฉันไม่รู้ว่าทำไมคำตอบของฉันถึงได้รับค่าเหมืองเพราะคำตอบของฉันเป็นจริงดูลิงค์นี้มันเป็นจริงก่อนที่จะรับเหมืองไปยังคำตอบของฉันอ่านเพิ่มเติมstackoverflow.com/questions/5564410/difference-between-and
sara Sodagari
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.