เท่ากับ vs Arrays.equals ใน Java


209

เมื่อเปรียบเทียบอาร์เรย์ใน Java มีความแตกต่างระหว่าง 2 คำสั่งต่อไปนี้หรือไม่?

Object[] array1, array2;
array1.equals(array2);
Arrays.equals(array1, array2);

และถ้าเป็นเช่นนั้นพวกเขาคืออะไร?


ลองดูที่ java.util.Arrays.deepEquals (Object [] a1, Object [] a2)
ultraon

คำตอบ:


299

array1.equals(array2)เป็นเช่นเดียวกับarray1 == array2คือมันเป็นอาร์เรย์เดียวกัน @alf ชี้ให้เห็นว่าไม่ใช่สิ่งที่คนส่วนใหญ่คาดหวัง

Arrays.equals(array1, array2) เปรียบเทียบเนื้อหาของอาร์เรย์


ในทำนองเดียวกันอาจจะไม่เป็นประโยชน์มากและคุณจำเป็นต้องใช้array.toString()Arrays.toString(array)


59
โปรดทราบว่าArrays.equals()ไม่ทำงานตามที่คาดไว้สำหรับอาร์เรย์หลายมิติจะเปรียบเทียบเฉพาะรายการในมิติที่ 1 เพื่อความเท่าเทียมกันในการอ้างอิง Apache Commons ArrayUtils.isEqualsทำงานร่วมกับอาร์เรย์หลายมิติ
Adam Parkin

4
ฉันตกตะลึง มีเหตุผลสำหรับ array.equals ที่จะนำไปใช้กับการเปรียบเทียบตัวชี้แทนที่จะเปรียบเทียบความยาวและทุกวัตถุ?
ทะเลสาบ

2
@Lake จะเปรียบเทียบความยาวของอาร์เรย์และวัตถุที่มีอยู่ แต่สิ่งที่ไม่ได้ทำคือการเปรียบเทียบแบบลึก ความจริงที่เท่าเทียมกันใช้งานได้ตามที่คาดไว้สำหรับอาร์เรย์ที่ใช้งานไม่ได้นี่ไม่ควรเป็นปัญหาในตอนแรก
Peter Lawrey

48
@AdamParkin Arrays.deepEquals(Object[], Object[])ว่าเหตุผลที่เรามี
Elliott Frisch

3
@JeewanthaSamaraweera นั่นคือคำจำกัดความสำหรับวิธีการนั้น แต่.equalsมันไม่ได้เปรียบเทียบเนื้อหาซึ่งเป็นสาเหตุที่คุณต้องใช้วิธีการนั้น
ปีเตอร์ Lawrey

86

มันเป็นปัญหาที่น่าอับอาย: .equals()สำหรับอาร์เรย์นั้นใช้งานไม่ได้ใช้งานไม่ได้เลย

ที่กล่าวว่าจะไม่ "แตก" เช่นเดียวกับใน "ใครบางคนทำมันในทางที่ผิดจริง ๆ " - เพียงแค่ทำสิ่งที่กำหนดไว้และไม่ใช่สิ่งที่คาดหวัง ดังนั้นสำหรับคนพิถีพิถัน: มันดีมากและนั่นก็หมายความว่าอย่าใช้มันเลย

ตอนนี้พฤติกรรมที่คาดหวังสำหรับequalsคือการเปรียบเทียบข้อมูล พฤติกรรมเริ่มต้นคือการเปรียบเทียบตัวตนเช่นเดียวกับที่Objectไม่มีข้อมูลใด ๆ (สำหรับคนพิถีพิถัน: ใช่มันมี แต่ไม่ใช่จุด); สมมติว่าถ้าคุณต้องการequalsในคลาสย่อยคุณจะใช้มัน ในอาร์เรย์ไม่มีการใช้งานสำหรับคุณดังนั้นคุณจึงไม่ควรใช้มัน

ดังนั้นความแตกต่างคือใช้Arrays.equals(array1, array2)งานได้ตามที่คุณคาดหวัง (เช่นเปรียบเทียบเนื้อหา) array1.equals(array2)กลับสู่Object.equalsการนำไปใช้ซึ่งจะเปรียบเทียบตัวตนและแทนที่ด้วยดีกว่า==(สำหรับผู้พิเคราะห์: ใช่ฉันรู้null)

ปัญหาคือแม้Arrays.equals(array1, array2)จะกัดคุณอย่างหนักหากองค์ประกอบของอาเรย์ไม่สามารถใช้งานequalsได้อย่างถูกต้อง มันเป็นคำสั่งที่ไร้เดียงสามากฉันรู้ แต่มีกรณีที่สำคัญน้อยกว่าที่เห็นได้ชัดมาก: พิจารณาอาร์เรย์ 2 มิติ

อาร์เรย์ 2 มิติใน Java เป็นอาร์เรย์ของอาร์เรย์และอาร์เรย์ ' equalsเสีย (หรือไร้ประโยชน์หากคุณต้องการ) ดังนั้นArrays.equals(array1, array2)จะไม่ทำงานตามที่คุณคาดหวังใน 2D อาร์เรย์

หวังว่าจะช่วย


13
มันไม่แตกมันแค่สืบทอดมาจาก Object
Michael Borgwardt

อาร์เรย์มีการใช้งานที่กำหนดเองequals()หรือไม่? ฉันคิดว่าไม่ได้ถูกแทนที่จาก Object
Martijn Courteaux

@MichaelBorgwardt เป็นไลบรารีระบบด้วยวิธีที่ไม่ทำสิ่งที่พูดใน javadoc เสียงแตกพอสำหรับฉัน ที่กล่าวว่าฉันยอมรับว่ามันเป็นคำพูดที่ถกเถียงกันมาก แต่ฉันเชื่อว่า "มันเสีย" จำได้ดีกว่าและทำให้สะดวกกว่าที่จะคิดแบบนี้
alf

@MartijnCourteaux ที่ว่าปัญหา :)
alf

3
สำหรับอาร์เรย์ของอาร์เรย์คุณต้องมีArrays.deepEquals--- มันเป็นสิ่งที่someArray.equalsควรทำมาตลอด (ที่เกี่ยวข้อง: Objects.deepEquals.)
Kevin J. Chase

16

ดูการใช้งานของทั้งสองวิธีเพื่อทำความเข้าใจอย่างลึกซึ้ง:

array1.equals(array2);
/**
 * Indicates whether some other object is "equal to" this one.
 * <p>
 * The {@code equals} method implements an equivalence relation
 * on non-null object references:
 * <ul>
 * <li>It is <i>reflexive</i>: for any non-null reference value
 *     {@code x}, {@code x.equals(x)} should return
 *     {@code true}.
 * <li>It is <i>symmetric</i>: for any non-null reference values
 *     {@code x} and {@code y}, {@code x.equals(y)}
 *     should return {@code true} if and only if
 *     {@code y.equals(x)} returns {@code true}.
 * <li>It is <i>transitive</i>: for any non-null reference values
 *     {@code x}, {@code y}, and {@code z}, if
 *     {@code x.equals(y)} returns {@code true} and
 *     {@code y.equals(z)} returns {@code true}, then
 *     {@code x.equals(z)} should return {@code true}.
 * <li>It is <i>consistent</i>: for any non-null reference values
 *     {@code x} and {@code y}, multiple invocations of
 *     {@code x.equals(y)} consistently return {@code true}
 *     or consistently return {@code false}, provided no
 *     information used in {@code equals} comparisons on the
 *     objects is modified.
 * <li>For any non-null reference value {@code x},
 *     {@code x.equals(null)} should return {@code false}.
 * </ul>
 * <p>
 * The {@code equals} method for class {@code Object} implements
 * the most discriminating possible equivalence relation on objects;
 * that is, for any non-null reference values {@code x} and
 * {@code y}, this method returns {@code true} if and only
 * if {@code x} and {@code y} refer to the same object
 * ({@code x == y} has the value {@code true}).
 * <p>
 * Note that it is generally necessary to override the {@code hashCode}
 * method whenever this method is overridden, so as to maintain the
 * general contract for the {@code hashCode} method, which states
 * that equal objects must have equal hash codes.
 *
 * @param   obj   the reference object with which to compare.
 * @return  {@code true} if this object is the same as the obj
 *          argument; {@code false} otherwise.
 * @see     #hashCode()
 * @see     java.util.HashMap
 */
public boolean equals(Object obj) {
    return (this == obj);
}

ในขณะที่:

Arrays.equals(array1, array2);
/**
 * Returns <tt>true</tt> if the two specified arrays of Objects are
 * <i>equal</i> to one another.  The two arrays are considered equal if
 * both arrays contain the same number of elements, and all corresponding
 * pairs of elements in the two arrays are equal.  Two objects <tt>e1</tt>
 * and <tt>e2</tt> are considered <i>equal</i> if <tt>(e1==null ? e2==null
 * : e1.equals(e2))</tt>.  In other words, the two arrays are equal if
 * they contain the same elements in the same order.  Also, two array
 * references are considered equal if both are <tt>null</tt>.<p>
 *
 * @param a one array to be tested for equality
 * @param a2 the other array to be tested for equality
 * @return <tt>true</tt> if the two arrays are equal
 */
public static boolean equals(Object[] a, Object[] a2) {
    if (a==a2)
        return true;
    if (a==null || a2==null)
        return false;

    int length = a.length;
    if (a2.length != length)
        return false;

    for (int i=0; i<length; i++) {
        Object o1 = a[i];
        Object o2 = a2[i];
        if (!(o1==null ? o2==null : o1.equals(o2)))
            return false;
    }

    return true;
}

11

ถอนหายใจ ย้อนกลับไปในยุค 70 ฉันเป็น "โปรแกรมเมอร์ระบบ" (ดูแลระบบ) สำหรับระบบ IBM 370 และนายจ้างของฉันเป็นสมาชิกของกลุ่มผู้ใช้ IBM แบ่งปัน SHARE บางครั้งมันจะเกิดขึ้นที่ใครบางคนส่ง APAR (รายงานข้อผิดพลาด) เกี่ยวกับพฤติกรรมที่ไม่คาดคิดของคำสั่ง CMS บางอย่างและ IBM จะตอบสนอง NOTABUG: คำสั่งทำในสิ่งที่มันถูกออกแบบมาเพื่อทำ

แบ่งปันมาพร้อมกับเคาน์เตอร์: BAD - Broken As Design ฉันคิดว่านี่อาจนำไปใช้กับการดำเนินการเท่ากับสำหรับอาร์เรย์

ไม่มีอะไรผิดปกติกับการใช้ Object.equals วัตถุไม่มีข้อมูลสมาชิกดังนั้นจึงไม่มีอะไรให้เปรียบเทียบ "Object" สองรายการมีค่าเท่ากันหากเป็นจริงวัตถุเดียวกัน (ภายในที่อยู่และความยาวเดียวกัน)

แต่ตรรกะนั้นใช้ไม่ได้กับอาร์เรย์ อาร์เรย์มีข้อมูลและคุณคาดหวังการเปรียบเทียบ (ผ่านเท่ากับ) เพื่อเปรียบเทียบข้อมูล ตามหลักการแล้ววิธีที่ Arrays.deepEquals ทำ แต่อย่างน้อยที่สุดวิธีที่ Arrays.equals ทำ (การเปรียบเทียบองค์ประกอบของน้ำตื้น)

ดังนั้นปัญหาคืออาเรย์ (เป็นวัตถุในตัว) ไม่ได้แทนที่ Object.equals String (เป็นคลาสที่มีชื่อ) จะแทนที่ Object.equals และให้ผลลัพธ์ที่คุณคาดหวัง

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


5
ฉันชอบ Broken.As. ย่อตัวอักษร
Chris

5

อาร์เรย์สืบทอดequals()มาจากObjectและด้วยเหตุนี้การเปรียบเทียบจะส่งกลับค่าจริงเมื่อเปรียบเทียบอาร์เรย์กับตัวเองเท่านั้น

ในอีกทางหนึ่งให้Arrays.equalsเปรียบเทียบองค์ประกอบของอาร์เรย์

ตัวอย่างนี้อธิบายความแตกต่าง:

Object o1 = new Object();
Object o2 = new Object();
Object[] a1 = { o1, o2 };
Object[] a2 = { o1, o2 };
System.out.println(a1.equals(a2)); // prints false
System.out.println(Arrays.equals(a1, a2)); // prints true

Arrays.equals()ดูเพิ่มเติม Arrays.deepEquals()อีกวิธีที่คงที่นอกจากนี้ยังอาจจะเป็นที่น่าสนใจ:


1

The Arrays.equals(array1, array2):

ตรวจสอบว่าทั้งสองอาร์เรย์มีจำนวนองค์ประกอบเท่ากันหรือไม่และคู่ขององค์ประกอบที่สอดคล้องกันทั้งหมดในสองอาร์เรย์นั้นเท่ากันหรือไม่

The array1.equals(array2):

เปรียบเทียบวัตถุกับวัตถุอื่นและคืนค่าจริงก็ต่อเมื่อการอ้างอิงของวัตถุทั้งสองมีค่าเท่ากับใน Object.equals()


0

equals()ของอาร์เรย์จะรับมาจากObjectจึงไม่ได้ดูที่เนื้อหาของ arrrays ก็จะพิจารณาแต่ละอาร์เรย์เท่ากับตัวเอง

Arrays.equals()วิธีการจะเปรียบเทียบเนื้อหาอาร์เรย์ มี overloads สำหรับทุกสภาพดั้งเดิมของและหนึ่งสำหรับวัตถุที่ใช้เองวัตถุequals()วิธีการ


2
คุณพูดว่า "เนื้อหาของอาร์เรย์" นี่หมายความว่าอาร์เรย์หลายมิติหรือไม่
AlanFoster

@AlanFoster: ไม่ อาร์เรย์หลายมิติเป็นอาร์เรย์ของอาร์เรย์ซึ่งหมายความว่าวิธีการของอาร์เรย์ (วัตถุ [], วัตถุ []) จะถูกเรียกใช้ซึ่งเรียกวิธีการของอาร์เรย์ย่อย () วิธีการ
ไมเคิล Borgwardt

0
import java.util.Arrays;
public class ArrayDemo {
   public static void main(String[] args) {
   // initializing three object arrays
   Object[] array1 = new Object[] { 1, 123 };
   Object[] array2 = new Object[] { 1, 123, 22, 4 };
   Object[] array3 = new Object[] { 1, 123 };

   // comparing array1 and array2
   boolean retval=Arrays.equals(array1, array2);
   System.out.println("array1 and array2 equal: " + retval);
   System.out.println("array1 and array2 equal: " + array1.equals(array2));

   // comparing array1 and array3
   boolean retval2=Arrays.equals(array1, array3);
   System.out.println("array1 and array3 equal: " + retval2);
   System.out.println("array1 and array3 equal: " + array1.equals(array3));

   }
}

นี่คือผลลัพธ์:

    array1 and array2 equal: false
    array1 and array2 equal: false

    array1 and array3 equal: true
    array1 and array3 equal: false

เมื่อเห็นปัญหาแบบนี้ฉันจะArrays.equals(array1, array2)ตอบคำถามของคุณเป็นการส่วนตัวเพื่อหลีกเลี่ยงความสับสน


ดูเหมือนว่าจะถูกต้อง แต่ในอาร์เรย์ลำดับขององค์ประกอบก็มีความสำคัญเช่นกัน ตัวอย่างเช่นหากคุณมีวัตถุอาร์เรย์ [] array4 = วัตถุใหม่ [] {123, 1}; ด้วย Arrays.equals (array3, array4) มันจะส่งคืนค่าเท็จ
jiahao
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.