Java: Integer เท่ากับ vs. ==


152

ในฐานะของ Java 1.5 คุณสามารถแลกเปลี่ยนIntegerกับintสถานการณ์ได้มากมาย

อย่างไรก็ตามฉันพบข้อบกพร่องที่อาจเกิดขึ้นในรหัสของฉันที่ทำให้ฉันประหลาดใจเล็กน้อย

รหัสต่อไปนี้:

Integer cdiCt = ...;
Integer cdsCt = ...;
...
if (cdiCt != null && cdsCt != null && cdiCt != cdsCt)
    mismatch = true;

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

การเปลี่ยนเงื่อนไขเป็น:

if (cdiCt != null && cdsCt != null && !cdiCt.equals(cdsCt))

แก้ไขปัญหา

มีใครเล่าให้ฟังว่าทำไมสิ่งนี้ถึงเกิดขึ้นได้บ้าง? จนถึงตอนนี้ฉันเพิ่งเห็นพฤติกรรมบนโลคัลโฮสต์ของฉันบนพีซีของฉันเอง ในกรณีนี้รหัสทำให้สำเร็จในการเปรียบเทียบประมาณ 20 ครั้ง แต่ล้มเหลวในวันที่ 2 ปัญหานี้สามารถทำซ้ำได้อย่างสม่ำเสมอ

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

มันยังไม่ถูกต้องหรือไม่ที่จะใช้==เพื่อเปรียบเทียบสองIntegerค่า

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

ทำไมคอมไพเลอร์ / JVM ไม่สามารถทำการ autoboxing“ แค่ทำงาน” ได้?

คำตอบ:


238

JVM กำลังแคชค่าจำนวนเต็ม == ใช้ได้กับตัวเลขระหว่าง -128 ถึง 127 เท่านั้น http://www.owasp.org/index.php/Java_gotchas#Immutable_Objects_.2F_Wrapper_Class_Caching


1
ขอบคุณนั่นอธิบายได้อย่างชัดเจนว่าทำไม 137 ล้มเหลว! และมันก็ตอบคำถามของฉันเกี่ยวกับสาเหตุที่ไม่ใช่ปัญหาที่แพร่หลายใน 95% ของกรณีที่ฉันจะพบค่าจะอยู่ภายใต้ 127 ดีที่จะจับมันตอนนี้ แต่สำหรับ 5% ที่มันไม่ใช่
Jeremy Goodell

1
หมายเหตุด้านที่น่าสนใจ: จนถึงสองสามสัปดาห์ที่ผ่านมา cdiCt และ cdsCt ต่างก็เป็นเหมือนกัน แต่ฉันต้องทำให้เป็นจำนวนเต็มเพื่อตรวจสอบสถานการณ์ว่างซึ่งจัดการแตกต่างกัน ...
Jeremy Goodell

3
@ Jeremy ใช่มันเป็นปัญหาที่ค่อนข้างคลุมเครือ แต่โดยทั่วไปแล้วคุณใช้. Equals () สำหรับ Objects และ == สำหรับ primitives คุณไม่สามารถพึ่งพาการรับรองโดยอัตโนมัติในการทดสอบความเท่าเทียมกัน
อดัม

1
แล้วทำเครื่องหมายกลับมาหาคุณแล้ว! ดูเหมือนว่าโคลินจะมีคะแนนมากกว่าเพียงพออยู่แล้ว
Jeremy Goodell

2
โปรดทราบว่า Integer ใหม่ (1)! = new Integer (1) เช่นกัน ใหม่ ALWAYS ส่งคืนที่อยู่ใหม่ การล็อกอัตโนมัติใช้เวอร์ชันที่แคชไว้ วิธีอื่นที่คืนค่าจำนวนเต็ม (โดยไม่ต้องสร้างใหม่) อาจส่งคืนค่าที่แคชไว้เช่นกัน
Bill K

77

คุณไม่สามารถเปรียบเทียบสองรายการIntegerกับ==วัตถุแบบง่าย ๆได้ดังนั้นการอ้างอิงส่วนใหญ่จะไม่เหมือนกัน

มีเคล็ดลับคือIntegerระหว่าง -128 ถึง 127 การอ้างอิงจะเหมือนกับการใช้การล็อกอัตโนมัติInteger.valueOf()ซึ่งแคชจำนวนเต็มเล็กน้อย

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


ทรัพยากร:

ในหัวข้อเดียวกัน:


1
การรับประกันจาก JLS หรือเพียงเพื่อ Oracle JVM หรือไม่
Thorbjørn Ravn Andersen

ส่วนที่ยกมานั้นมาจาก JLS ดังนั้นจึงเป็นการรับประกันจาก JLS
Colin Hebert

เรื่องการรับประกัน ฉันยังไม่เชื่อใจมันมากเกินไป new Integer(1) == new Integer(1)ยังคงเป็นเท็จ
Thilo

@Thilo อยู่เสมอnew ... == new ... false
MC Emperor

2
@Thilo True ใช้เสมอequals()เมื่อจัดการกับวัตถุ นี่เป็นหนึ่งในสิ่งแรกที่เราควรรู้เมื่อเรียนรู้ Java โดยวิธีการที่ฉันจะเดาได้ว่าตัวสร้างของIntegerเป็นส่วนตัวนั่นคือกรณีที่ถูกสร้างขึ้นผ่านvalueOf()วิธีการเสมอ แต่ฉันเห็นว่าตัวสร้างเป็นแบบสาธารณะ
MC Emperor

5

ปัญหาคือวัตถุทั้งสองของคุณเป็นเพียงวัตถุ ไม่ตรงกันเนื่องจากคุณกำลังเปรียบเทียบการอ้างอิงวัตถุสองรายการของคุณไม่ใช่ค่าภายใน เห็นได้ชัดว่า.equalsถูกแทนที่เพื่อให้การเปรียบเทียบค่าเมื่อเทียบกับการเปรียบเทียบการอ้างอิงวัตถุ


คำตอบที่ดี แต่มันไม่ได้อธิบายว่าทำไมมันล้มเหลวเพียง 137 ข้อเท่านั้น
Jeremy Goodell

4

Integerหมายถึงการอ้างอิงนั่นคือเมื่อเปรียบเทียบการอ้างอิงคุณกำลังเปรียบเทียบว่าพวกเขาชี้ไปที่วัตถุเดียวกันไม่ใช่ค่า ดังนั้นปัญหาที่คุณเห็น เหตุผลที่มันได้ผลดีกับที่ราบintประเภทคือว่ามัน unboxes Integerค่าที่มีอยู่โดย

ฉันขอเพิ่มได้ไหมถ้าคุณทำสิ่งที่คุณกำลังทำอยู่ทำไมต้องมีifคำสั่งให้เริ่มด้วย?

mismatch = ( cdiCt != null && cdsCt != null && !cdiCt.equals( cdsCt ) );

4

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

จำนวนเต็มใช้แคชจำนวนเต็มเพื่อจัดเก็บค่าจาก -128 ถึง +127 หากมีการใช้ตัวดำเนินการ == เพื่อตรวจสอบค่าใด ๆ ระหว่าง -128 ถึง 127 ดังนั้นจะส่งคืนค่าจริง สำหรับค่าอื่นที่ไม่ใช่ค่าเหล่านี้จะส่งคืนค่าเท็จ

อ้างอิงลิงค์สำหรับข้อมูลเพิ่มเติมบางอย่าง


1

เช่นกันสำหรับความถูกต้องของการใช้==คุณสามารถยกเลิกการเปรียบเทียบIntegerค่าใดค่าหนึ่งก่อนที่จะทำการ==เปรียบเทียบเช่น:

if ( firstInteger.intValue() == secondInteger ) {..

ที่สองจะถูกยกเลิกการทำกล่องอัตโนมัติ (แน่นอนว่าคุณต้องตรวจสอบnullก่อน)


0

นอกจากคำตอบที่ได้รับเหล่านี้สิ่งที่ฉันได้เรียนรู้คือ:

ไม่เปรียบเทียบวัตถุด้วย == เว้นแต่ว่าคุณต้องการเปรียบเทียบโดยอ้างอิง

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