ทำไม 128 == 128 false แต่ 127 == 127 เป็นจริงเมื่อเปรียบเทียบ Integer wrappers ใน Java


172
class D {
    public static void main(String args[]) {
        Integer b2=128;
        Integer b3=128;
        System.out.println(b2==b3);
    }
}

เอาท์พุท:

false

class D {
    public static void main(String args[]) {
        Integer b2=127;
        Integer b3=127;
        System.out.println(b2==b3);
    }
}

เอาท์พุท:

true

หมายเหตุ: ตัวเลขระหว่าง -128 ถึง 127 เป็นจริง


10
คุณอาจพบข้อมูลbexhuff.com/2006/11/java-1-5-autoboxing-wackyness
Dominic Rodger

1
คุณมาถึงจุดที่จะถามคำถามนี้ได้อย่างไร มันสนุกจริงๆ แต่ไม่มีใครเคยเจออะไรแบบนั้น "ในโลกแห่งความเป็นจริง" ... หรือ?
Mare Infinitus

คำตอบ:


217

เมื่อคุณรวบรวมตัวเลขตามตัวอักษรใน Java และกำหนดให้กับ Integer (ตัวพิมพ์ใหญ่I) คอมไพเลอร์จะปล่อย:

Integer b2 =Integer.valueOf(127)

บรรทัดของรหัสนี้จะถูกสร้างขึ้นเช่นกันเมื่อคุณใช้การล็อกอัตโนมัติ

valueOf มีการใช้งานโดยที่ตัวเลขบางตัวเป็น "พูล" และจะส่งคืนอินสแตนซ์เดียวกันสำหรับค่าที่น้อยกว่า 128

จากซอร์สโค้ด Java 1.6, บรรทัด 621:

public static Integer valueOf(int i) {
    if(i >= -128 && i <= IntegerCache.high)
        return IntegerCache.cache[i + 128];
    else
        return new Integer(i);
}

ค่าของhighสามารถกำหนดค่าเป็นค่าอื่นด้วยคุณสมบัติของระบบ

-Djava.lang.Integer.IntegerCache.high = 999

หากคุณรันโปรแกรมด้วยคุณสมบัติของระบบมันจะออกจริง!

ข้อสรุปที่ชัดเจน: ไม่ต้องอ้างอิงสองคนที่เหมือนกันเสมอเปรียบเทียบกับ.equals()วิธี

ดังนั้นb2.equals(b3)จะพิมพ์ค่าจริงสำหรับค่า b2, b3

โปรดทราบว่าIntegerแคชไม่ได้มีไว้สำหรับเหตุผลด้านประสิทธิภาพ แต่เพื่อให้สอดคล้องกับJLS หัวข้อ 5.1.7 ; ต้องระบุเอกลักษณ์ของวัตถุสำหรับค่า -128 ถึง 127

จำนวนเต็ม # valueOf (int)ยังจัดทำเอกสารพฤติกรรมนี้:

วิธีนี้น่าจะให้ประสิทธิภาพพื้นที่และเวลาได้ดีขึ้นอย่างมีนัยสำคัญโดยการแคชค่าที่ร้องขอบ่อยๆ วิธีการนี้จะทำการแคชค่าในช่วง -128 ถึง 127, รวมและอาจแคชค่าอื่น ๆ ที่อยู่นอกช่วงนี้


1
โปรดทราบว่าค่าที่น้อยกว่า 127 จะถูกละเว้นโดย java และค่าที่ใหญ่กว่า Integer.MAX_VALUE-128 จะถูก จำกัด
Andreas Petersson

จำนวนเต็มถูกแคชสำหรับค่าไบต์ใน Java 5 และสูงกว่าทำให้ Integer ใหม่ (1) == ใหม่จำนวนเต็ม (1) อย่างไรก็ตามนี่ไม่ใช่กรณีใน Java 1.4 หรือต่ำกว่าดังนั้นโปรดระวังหากคุณต้องปรับลดรุ่นเป็นสภาพแวดล้อมในที่สุด
MetroidFan2002

11
ไม่ผิดนี่เป็นความผิด new Integer (1) == new Integer (1) เป็นเท็จโดยไม่คำนึงถึง jvm AFAIK ไม่มีคอมไพเลอร์จะโกงที่คำหลัก "ใหม่" มันจะต้องยกตัวอย่างวัตถุใหม่เสมอ
Andreas Petersson

1
@ Holger จุดที่น่าสนใจ แต่เป็นไปได้ในทางเทคนิคที่จะแทนที่คลาส Integer จาก JDK ด้วยความหมายที่กำหนดเอง ... (อย่าถามว่าทำไมใครบางคนถึงเป็นบ้า) - จากนั้นมันอาจมีผลข้างเคียงที่ไม่ได้รับอนุญาตให้ปรับให้เหมาะสม
Andreas Petersson

1
@ AndreasPetersson แน่นอน “ คอมไพเลอร์” หมายถึงคอมไพเลอร์ของ JIT ซึ่งจะรู้คลาสการใช้งานจริงและอาจปรับให้เหมาะสมเท่านั้นหากตัวสร้างไม่มีผลข้างเคียง หรือเพิ่มประสิทธิภาพของการแสดงออกในการทำซ้ำเพียง falseแต่มีผลข้างเคียงตามมาด้วยการใช้ ที่จริงแล้วสิ่งนี้อาจเกิดขึ้นได้แล้วในวันนี้เนื่องจากเป็นผลข้างเคียงของการใช้การวิเคราะห์การหลบหนีและการแทนที่ด้วยสเกลาร์
Holger

24

แคชออโต้บ็อกซ์ -128 ถึง 127 ซึ่งระบุไว้ใน JLS ( 5.1.7 )

หากค่า p ที่ถูกบรรจุอยู่เป็นจริง, เท็จ, ไบต์, ตัวอักษรในช่วง \ u0000 ถึง \ u007f หรือจำนวน int หรือสั้นระหว่าง -128 ถึง 127 จากนั้นให้ r1 และ r2 เป็นผลของการแปลงมวยสองครั้ง ของ p. เป็นกรณีที่ r1 == r2 เสมอ

กฎง่ายๆที่ต้องจำเมื่อจัดการกับวัตถุคือ - ใช้.equalsถ้าคุณต้องการตรวจสอบว่าวัตถุทั้งสองเป็น "เท่ากัน" ใช้==เมื่อคุณต้องการดูว่าพวกเขาชี้ไปที่อินสแตนซ์เดียวกัน


1
หมายเหตุ: JLS เปลี่ยนแปลงใน Java 9 ตอนนี้รับประกันเฉพาะสำหรับนิพจน์ค่าคงที่เวลาคอมไพล์เท่านั้น ดูอัปเดตเป็นคำตอบที่ยอมรับได้
สตีเฟ่นซี

9

การใช้ชนิดข้อมูลดั้งเดิม ints จะสร้างจริงในทั้งสองกรณีผลลัพธ์ที่คาดหวัง

อย่างไรก็ตามเนื่องจากคุณกำลังใช้วัตถุจำนวนเต็มตัวดำเนินการ == จึงมีความหมายแตกต่างกัน

ในบริบทของวัตถุ == ตรวจสอบเพื่อดูว่าตัวแปรอ้างอิงถึงการอ้างอิงวัตถุเดียวกัน

ในการเปรียบเทียบค่าของวัตถุคุณควรใช้เมธอด equals () เช่น

 b2.equals(b1)

ซึ่งจะระบุว่า b2 น้อยกว่า b1 มากกว่าหรือเท่ากับ (ตรวจสอบรายละเอียด API)


7

เป็นการเพิ่มประสิทธิภาพหน่วยความจำใน Java ที่เกี่ยวข้อง

เพื่อบันทึกในหน่วยความจำ Java 'reuses' วัตถุห่อหุ้มทั้งหมดที่มีค่าอยู่ในช่วงต่อไปนี้:

ค่าบูลีนทั้งหมด (จริงและเท็จ)

ค่า Byte ทั้งหมด

ค่าอักขระทั้งหมดจาก \ u0000 ถึง \ u007f (เช่น 0 ถึง 127 ในรูปทศนิยม)

ค่า Short และ Integer ทั้งหมดจาก -128 ถึง 127


3

ดูที่ Integer.java ถ้าค่าอยู่ระหว่าง -128 ถึง 127 มันจะใช้ cached pool ดังนั้น(Integer) 1 == (Integer) 1ในขณะที่(Integer) 222 != (Integer) 222

 /**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}       

0

คำตอบอื่น ๆ อธิบายถึงสาเหตุที่สามารถสังเกตได้ถึงผลกระทบที่สังเกตได้ แต่นั่นเป็นจุดที่นักเขียนโปรแกรม (น่าสนใจแน่นอน แต่สิ่งที่คุณควรลืมเมื่อเขียนโค้ดจริง)

ในการเปรียบเทียบออบเจ็กต์ Integer เพื่อความเท่าเทียมใช้equalsเมธอด

==อย่าพยายามที่จะเปรียบเทียบวัตถุจำนวนเต็มเพื่อความเท่าเทียมกันโดยใช้ประกอบการประจำตัว

อาจเกิดขึ้นได้ว่าค่าเท่ากันบางอย่างเป็นวัตถุที่เหมือนกัน แต่นี่ไม่ใช่สิ่งที่ควรเป็นที่พึ่ง


-4

ฉันเขียนสิ่งต่อไปนี้เนื่องจากปัญหานี้ไม่ได้มีเฉพาะเจาะจงกับ Integer ข้อสรุปของฉันคือบ่อยกว่าถ้าคุณใช้ API ไม่ถูกต้องคุณจะเห็นพฤติกรรมที่ไม่ถูกต้อง ใช้อย่างถูกต้องและคุณควรเห็นพฤติกรรมที่ถูกต้อง:

public static void main (String[] args) {
    Byte b1=127;
    Byte b2=127;

    Short s1=127; //incorrect should use Byte
    Short s2=127; //incorrect should use Byte
    Short s3=128;
    Short s4=128;

    Integer i1=127; //incorrect should use Byte
    Integer i2=127; //incorrect should use Byte
    Integer i3=128;
    Integer i4=128;

    Integer i5=32767; //incorrect should use Short
    Integer i6=32767; //incorrect should use Short

    Long l1=127L;           //incorrect should use Byte
    Long l2=127L;           //incorrect should use Byte
    Long l3=13267L;         //incorrect should use Short
    Long l4=32767L;         //incorrect should use Short
    Long l5=2147483647L;    //incorrect should use Integer 
    Long l6=2147483647L;    //incorrect should use Integer
    Long l7=2147483648L;
    Long l8=2147483648L;

    System.out.print(b1==b2); //true  (incorrect) Used API correctly
    System.out.print(s1==s2); //true  (incorrect) Used API incorrectly
    System.out.print(i1==i2); //true  (incorrect) Used API incorrectly
    System.out.print(l1==l2); //true  (incorrect) Used API incorrectly

    System.out.print(s3==s4); //false (correct) Used API correctly
    System.out.print(i3==i4); //false (correct) Used API correctly
    System.out.print(i5==i6); //false (correct) Used API correctly
    System.out.print(l3==l4); //false (correct) Used API correctly
    System.out.print(l7==l8); //false (correct) Used API correctly
    System.out.print(l5==l6); //false (correct) Used API incorrectly

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.