วิธีที่ง่ายที่สุดในการเปรียบเทียบอาร์เรย์ใน C #


180

ใน Java Arrays.equals()อนุญาตให้เปรียบเทียบเนื้อหาของสองอาร์เรย์พื้นฐานได้อย่างง่ายดาย (มีการโหลดเกินสำหรับประเภทพื้นฐานทั้งหมด)

มีสิ่งนั้นใน C # หรือไม่ มีวิธี "วิเศษ" ในการเปรียบเทียบเนื้อหาของสองอาร์เรย์ใน C # หรือไม่


1
เพิ่ม '.net' ลงในแท็กเนื่องจากเทคนิคนี้สามารถใช้ในภาษาอื่น ๆ ที่คล้ายกันใน. net
Evan Plaice

3
สำหรับทุกคนที่อ่านสิ่งนี้โปรดทราบว่าคำตอบที่ยอมรับคือการใช้ SequenceEqual SequenceEqual ตรวจสอบไม่เพียง แต่ถ้าพวกเขามีข้อมูลเหมือนกัน แต่ถ้าพวกเขามีข้อมูลเดียวกันในลำดับเดียวกัน
จอห์น Demetriou

คำตอบ:


262

Enumerable.SequenceEqualคุณสามารถใช้ ใช้งานได้กับทุกIEnumerable<T>อย่างไม่เพียง แต่อาร์เรย์


สิ่งนี้ใช้ได้เฉพาะในกรณีที่อยู่ในลำดับเดียวกันเท่านั้น
John Demetriou

1
SequenceEqualอาจไม่ใช่ตัวเลือกที่ดีเนื่องจากการใช้งานในปัจจุบันอาจระบุแหล่งที่มาอย่างใดอย่างหนึ่งอย่างสมบูรณ์หากมีความยาวแตกต่างกันเท่านั้น ด้วยอาร์เรย์เราสามารถตรวจสอบความเท่าเทียมกันเป็นครั้งแรกในการสั่งซื้อเพื่อหลีกเลี่ยงแจงอาร์เรย์ของความยาวที่แตกต่างกันเพียงแค่ปลายขึ้นผลผลิตLength false
Frédéric

72

ใช้Enumerable.SequenceEqualในLINQ

int[] arr1 = new int[] { 1,2,3};
int[] arr2 = new int[] { 3,2,1 };

Console.WriteLine(arr1.SequenceEqual(arr2)); // false
Console.WriteLine(arr1.Reverse().SequenceEqual(arr2)); // true

1
โปรดทราบว่าสิ่งนี้จะส่งผลให้เกิดข้อโต้แย้งที่เป็นโมฆะดังนั้นอย่าคาดเดาว่าจะเป็นเช่นนั้นnew int[] {1}.SequenceEquals(null) == false
sara

30

นอกจากนี้สำหรับอาร์เรย์ (และ tuples) คุณสามารถใช้อินเตอร์เฟซใหม่จาก .NET 4.0: IStructuralComparableและIStructuralEquatable ใช้พวกเขาไม่เพียง แต่สามารถตรวจสอบความเท่าเทียมกันของอาร์เรย์ แต่ยังเปรียบเทียบพวกเขา

static class StructuralExtensions
{
    public static bool StructuralEquals<T>(this T a, T b)
        where T : IStructuralEquatable
    {
        return a.Equals(b, StructuralComparisons.StructuralEqualityComparer);
    }

    public static int StructuralCompare<T>(this T a, T b)
        where T : IStructuralComparable
    {
        return a.CompareTo(b, StructuralComparisons.StructuralComparer);
    }
}

{
    var a = new[] { 1, 2, 3 };
    var b = new[] { 1, 2, 3 };
    Console.WriteLine(a.Equals(b)); // False
    Console.WriteLine(a.StructuralEquals(b)); // True
}
{
    var a = new[] { 1, 3, 3 };
    var b = new[] { 1, 2, 3 };
    Console.WriteLine(a.StructuralCompare(b)); // 1
}

ให้อภัยฉันควรจะเป็น 1 หรือ 0 ในa.StructuralCompare(b)?
mafu

ในอาร์เรย์ประเภทค่าขนาดใหญ่จะมีประสิทธิภาพในการใช้งานเนื่องจากการใช้งานในปัจจุบันของพวกเขาจะทำกล่องแต่ละค่าเพื่อเปรียบเทียบ
Frédéric

18

สำหรับ. NET 4.0และสูงกว่าคุณสามารถเปรียบเทียบองค์ประกอบในอาร์เรย์หรือสิ่งอันดับผ่านการใช้ประเภทโครงสร้างการเปรียบเทียบ :

object[] a1 = { "string", 123, true };
object[] a2 = { "string", 123, true };

Console.WriteLine (a1 == a2);        // False (because arrays is reference types)
Console.WriteLine (a1.Equals (a2));  // False (because arrays is reference types)

IStructuralEquatable se1 = a1;
//Next returns True
Console.WriteLine (se1.Equals (a2, StructuralComparisons.StructuralEqualityComparer)); 

แก้ไข: พูดเร็วเกินไป ฉันสามารถทำ StructualEqualityCompare กับ IStructuralComparable ได้หรือไม่? ฉันต้องการโทรเปรียบเทียบกับวัตถุสองแถวเพื่อหาว่าอันไหนที่ "แรก" ฉันลอง IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralEqualityComparer)); การรับ: ไม่สามารถแปลงจาก 'System.Collections.IEqualityComparer' เป็น 'System.Collections.IComparer'
shindigo

1
ตกลง - การโทรที่ถูกต้องคือ: IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralComparer));
shindigo

15

SequenceEqual จะคืนค่าจริงหากมีสองเงื่อนไขหรือเป็นไปตามเงื่อนไข

  1. พวกเขามีองค์ประกอบเดียวกัน
  2. องค์ประกอบอยู่ในลำดับเดียวกัน

หากคุณต้องการตรวจสอบว่าพวกเขามีองค์ประกอบเดียวกันโดยไม่คำนึงถึงลำดับของพวกเขาและปัญหาของคุณเป็นประเภท

values2 มีค่าทั้งหมดที่มีอยู่ใน values1 หรือไม่?

คุณสามารถใช้วิธีการขยาย LINQ Enumerable.Exceptแล้วตรวจสอบว่าผลลัพธ์มีค่าใด ๆ นี่คือตัวอย่าง

int[] values1 = { 1, 2, 3, 4 };
int[] values2 = { 1, 2, 5 };
var result = values1.Except(values2);
if(result.Count()==0)
{
   //They are the same
}
else
{
    //They are different
}

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

โปรดทราบว่าหากคุณเรียกใช้รหัสของคุณเช่นนี้

var result = values2.Except(values1);

คุณจะได้รับผลลัพธ์ที่แตกต่าง

ในกรณีของฉันฉันมีสำเนาของอาร์เรย์และต้องการตรวจสอบว่ามีสิ่งใดที่ถูกลบออกจากอาร์เรย์เดิมดังนั้นฉันจึงใช้วิธีนี้


2
อาร์เรย์ที่มีค่าเท่ากันในลำดับที่ต่างกันจะไม่เท่ากับ คุณสนใจ 'Demetriou' == 'uoirtemeD' หรือไม่
edc65

ขึ้นอยู่กับว่า หากคุณกำลังใช้อาร์เรย์เป็นคอลเลกชันที่ไม่ได้เรียงลำดับและจำเป็นต้องตรวจสอบว่าพวกเขามีองค์ประกอบเดียวกัน (เช่นค่าจากฐานข้อมูลกับรายการกำหนดค่า) นี่เป็นวิธีที่ง่ายที่สุดที่ฉันพบ หากคำสั่งไม่ก็ตาม (เช่นสตริง) SequenceEqualแล้วคุณจะใช้
อาร์มันโด

11

สำหรับการทดสอบหน่วยคุณสามารถใช้แทนCollectionAssert.AreEqualAssert.AreEqual

มันอาจเป็นวิธีที่ง่ายที่สุด


11

หากคุณต้องการจัดการnullอินพุตอย่างสุภาพและไม่สนใจลำดับของรายการลองวิธีแก้ปัญหาต่อไปนี้:

static class Extensions
{
    public static bool ItemsEqual<TSource>(this TSource[] array1, TSource[] array2)
    {
        if (array1 == null && array2 == null)
            return true;
        if (array1 == null || array2 == null)
            return false;
        return array1.Count() == array2.Count() && !array1.Except(array2).Any();
    }
}

รหัสทดสอบดูเหมือนว่า:

class Program
{
    static void Main(string[] args)
    {
        int[] a1 = new int[] { 1, 2, 3 };
        int[] a2 = new int[] { 3, 2, 1 };
        int[] a3 = new int[] { 1, 3 };
        int[] a4 = null;
        int[] a5 = null;
        int[] a6 = new int[0];

        Console.WriteLine(a1.ItemsEqual(a2)); // Output: True.
        Console.WriteLine(a2.ItemsEqual(a3)); // Output: False.
        Console.WriteLine(a4.ItemsEqual(a5)); // Output: True. No Exception.
        Console.WriteLine(a4.ItemsEqual(a3)); // Output: False. No Exception.
        Console.WriteLine(a5.ItemsEqual(a6)); // Output: False. No Exception.
    }
}

นี้จะเป็นประโยชน์สำหรับผม แต่ถ้าa1 = { 1, 1 }และa2 = { 1, 2 }แล้วการทดสอบครั้งแรกส่งกลับผลที่ไม่ถูกต้อง คำชี้แจงการคืนสินค้าควรเป็นreturn array1.Count() == array2.Count() && !array1.Except(array2).Any() && !array2.Except(array1).Any();
Polshgiant


2

โซลูชัน LINQ นี้ใช้งานได้ แต่ไม่แน่ใจว่าจะเปรียบเทียบประสิทธิภาพกับ SequenceEquals ได้อย่างไร แต่มันจัดการกับความยาวของอาร์เรย์ที่แตกต่างกันและ. All จะออกจากรายการแรกที่ไม่เท่ากันโดยไม่ต้องวนซ้ำทั่วทั้งอาร์เรย์

private static bool arraysEqual<T>(IList<T> arr1, IList<T> arr2)
        =>
            ReferenceEquals(arr1, arr2) || (
                arr1 != null && arr2 != null &&
                arr1.Count == arr2.Count &&
                arr1.Select((a, i) => arr2[i].Equals(a)).All(i => i)
            );

1

การเปรียบเทียบตามองค์ประกอบ เกี่ยวกับอะไร

public void Linq78a()
{
 int[] numbers1 = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
 int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
 bool bb = numbers.Zip(numbers1, (a, b) => (a == b)).Any(p => !p);
 if (!bb) Console.WriteLine("Lists are equal (bb)");
   else Console.WriteLine("Lists are not equal (bb)");
}

แทนที่เงื่อนไข (a == b) ด้วยสิ่งใดก็ตามที่คุณต้องการเปรียบเทียบใน a และ b

(ซึ่งรวมสองตัวอย่างจากนักพัฒนา Linq ตัวอย่าง Linq )


1
มันไม่ได้จัดการกับอาร์เรย์ที่มีความยาวต่างกัน (อาจให้ผลที่ไม่ถูกต้องtrue) และnullอาร์เรย์ (จะผิดพลาด)
Frédéric

1

ฉันทำสิ่งนี้ในสตูดิโอภาพและทำงานได้อย่างสมบูรณ์ การเปรียบเทียบดัชนีอาร์เรย์โดยดัชนีกับรหัสสั้น ๆ นี้

private void compareButton_Click(object sender, EventArgs e)
        {
            int[] answer = { 1, 3, 4, 6, 8, 9, 5, 4, 0, 6 };
            int[] exam = { 1, 2, 3, 6, 8, 9, 5, 4, 0, 7 };

            int correctAnswers = 0;
            int wrongAnswers = 0;

            for (int index = 0; index < answer.Length; index++)
            {
                if (answer[index] == exam[index])
                {
                    correctAnswers += 1;
                }
                else
                {
                    wrongAnswers += 1;
                }
            }

            outputLabel.Text = ("The matching numbers are " + correctAnswers +
                "\n" + "The non matching numbers are " + wrongAnswers);
        }

ผลลัพธ์จะเป็น; หมายเลขที่จับคู่คือ 7 หมายเลขที่ไม่ตรงกันคือ 3


2
มันไม่ได้จัดการกับอาร์เรย์ที่มีความยาวต่างกัน (จะผิดพลาด), nullอาร์เรย์ (จะมีปัญหาเช่นกัน) และมันทำอย่างอื่นนอกเหนือจากที่ OP ถาม เขาเพียงขอให้รู้ความเท่าเทียมกันโดยไม่นับจำนวนรายการที่แตกต่างหรือจับคู่
Frédéric

0

สมมติว่าความเท่าเทียมกันของอาร์เรย์หมายถึงอาร์เรย์ทั้งสองมีองค์ประกอบเท่ากันที่ดัชนีเท่ากันนั่นคือSequenceEqualคำตอบและIStructuralEquatableคำตอบคำตอบ

แต่ทั้งคู่มีข้อเสียประสิทธิภาพฉลาด

SequenceEqual การนำไปใช้ในปัจจุบันจะไม่เป็นทางลัดเมื่ออาร์เรย์มีความยาวแตกต่างกันดังนั้นจึงอาจนับหนึ่งในนั้นทั้งหมดโดยเปรียบเทียบแต่ละองค์ประกอบ

IStructuralEquatableไม่ใช่แบบทั่วไปและอาจทำให้เกิดการชกมวยของค่าที่เปรียบเทียบกัน ยิ่งไปกว่านั้นมันไม่ได้ใช้งานง่ายนักและเรียกร้องให้เข้ารหัสวิธีการช่วยเหลือบางอย่างที่ซ่อนมันไว้

มันอาจจะดีกว่าประสิทธิภาพที่ฉลาดในการใช้สิ่งที่ชอบ:

bool ArrayEquals<T>(T[] first, T[] second)
{
    if (first == second)
        return true;
    if (first == null || second == null)
        return false;
    if (first.Length != second.Length)
        return false;
    for (var i = 0; i < first.Length; i++)
    {
        if (first[i] != second[i])
            return false;
    }
    return true;
}

แต่แน่นอนว่านั่นไม่ใช่ "วิธีวิเศษ" ในการตรวจสอบความเท่าเทียมกันของอาเรย์

ดังนั้นในปัจจุบันไม่มีเลยไม่เท่ากับ Java Arrays.equals()ใน. Net


จะไม่เป็นครั้งแรกและครั้งที่สองที่ทั้งสองเป็นผลเป็นจริงหรือไม่ null == null ใช่ไหม
Jesse Williams

1
nullการทดสอบครั้งแรกจะกลับมาจริงถ้าทั้งสอง ประเด็นของคุณคืออะไร?
Frédéric

1
if (first [i]! = second [i]) จะไม่ทำงานกับ generics คุณต้องใช้หาก (! first [i] .Equals (second [i]))
Jack Griffin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.