web-dev-qa-db-fra.com

Comparaison de tableaux en C #

J'essaie de comparer deux tableaux l'un avec l'autre. J'ai essayé ce code et j'ai eu les erreurs suivantes.

static bool ArraysEqual(Array a1, Array a2)
{
    if (a1 == a2)
        return true;

    if (a1 == null || a2 == null)
        return false;

    if (a1.Length != a2.Length)
        return false;

    IList list1 = a1, list2 = a2; //error CS0305: Using the generic type 'System.Collections.Generic.IList<T>' requires '1' type arguments
    for (int i = 0; i < a1.Length; i++)
    {
        if (!Object.Equals(list1[i], list2[i])) //error CS0021: Cannot apply indexing with [] to an expression of type 'IList'(x2)
            return false;
    }
    return true;
}

Pourquoi ai-je cette erreur? Je suis allé chercher une solution de basse technologie et cela a bien fonctionné, mais je dois le copier/coller plusieurs fois pour chaque type.

static bool ArraysEqual(byte[] a1, byte[] a2)
{
    if (a1 == a2)
        return true;

    if (a1 == null || a2 == null)
        return false;

    if (a1.Length != a2.Length)
        return false;

    for (int i = 0; i < a1.Length; i++)
    {
        if (a1[i] != a2[i])
            return false;
    }
    return true;
}
58
user34537

À condition que vous ayez LINQ disponible et que vous ne vous préoccupiez pas trop des performances, la chose la plus simple est la suivante:

var arraysAreEqual = Enumerable.SequenceEqual(a1, a2);

En fait, il vaut probablement la peine de vérifier avec Reflector ou ILSpy ce que les méthodes SequenceEqual font réellement, car elles pourraient de toute façon être optimisées pour le cas particulier des valeurs de tableau!

152
Noldorin

"Pourquoi est-ce que je reçois cette erreur?" - probablement, vous n'avez pas "using System.Collections;" en haut du fichier - seulement "using System.Collections.Generic;" - cependant, les génériques sont probablement plus sûrs - voir ci-dessous:

static bool ArraysEqual<T>(T[] a1, T[] a2)
{
    if (ReferenceEquals(a1,a2))
        return true;

    if (a1 == null || a2 == null)
        return false;

    if (a1.Length != a2.Length)
        return false;

    EqualityComparer<T> comparer = EqualityComparer<T>.Default;
    for (int i = 0; i < a1.Length; i++)
    {
        if (!comparer.Equals(a1[i], a2[i])) return false;
    }
    return true;
}
72
Marc Gravell

Pour .NET 4.0 et les versions ultérieures, vous pouvez comparer des éléments de tableaux ou de nuplets en utilisant StructuralComparisons type:

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)); 
12
Yuliia Ashomok

Il est correct de recommander SequenceEqual, mais il est trop naïf de penser que cela peut être plus rapide que d’habitude pour la boucle (;;).

Voici le code reflété:

public static bool SequenceEqual<TSource>(this IEnumerable<TSource> first, 
    IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
    if (comparer == null)
    {
        comparer = EqualityComparer<TSource>.Default;
    }
    if (first == null)
    {
        throw Error.ArgumentNull("first");
    }
    if (second == null)
    {
        throw Error.ArgumentNull("second");
    }
    using (IEnumerator<TSource> enumerator = first.GetEnumerator())     
    using (IEnumerator<TSource> enumerator2 = second.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            if (!enumerator2.MoveNext() || !comparer.Equals(enumerator.Current, enumerator2.Current))
            {
                return false;
            }
        }
        if (enumerator2.MoveNext())
        {
            return false;
        }
    }
    return true;
}

Comme vous pouvez le constater, il utilise deux énumérateurs et déclenche de nombreux appels de méthode qui ralentissent considérablement le processus. En outre, il ne vérifie pas la longueur du tout, donc dans les cas difficiles, il peut être ridiculement plus lent.

Comparez déplacer deux itérateurs avec belle

if (a1[i] != a2[i])

et vous saurez ce que je veux dire par performance.

Il peut être utilisé dans les cas où les performances ne sont vraiment pas si critiques, peut-être dans le code de test unitaire, ou dans les cas de listes courtes dans des méthodes rarement appelées.

6
Valentin Kuzub

SequenceEqual peut être plus rapide. À savoir dans le cas où presque tout le temps, les deux tableaux ont bien la même longueur et ne sont pas le même objet.

Ce n'est toujours pas la même fonctionnalité que la fonction de l'OP, car il ne comparera pas les valeurs NULL de manière silencieuse.

2
Imi

Je sais que c’est un vieux sujet, mais je pense qu’il est toujours pertinent et je voudrais partager une implémentation d’une méthode de comparaison de matrice qui, j’estime, constitue le bon équilibre entre performance et élégance.

static bool CollectionEquals<T>(ICollection<T> a, ICollection<T> b, IEqualityComparer<T> comparer = null)
{
    return ReferenceEquals(a, b) || a != null && b != null && a.Count == b.Count && a.SequenceEqual(b, comparer);
}

L'idée ici est de vérifier d'abord toutes les conditions de sortie anticipée, puis de retomber sur SequenceEqual. Cela évite également de faire des branches supplémentaires et repose plutôt sur un court-circuit booléen pour éviter une exécution inutile. Je pense aussi que cela a l'air propre et qu'il est facile à comprendre.

De plus, en utilisant ICollection pour les paramètres, cela fonctionnera avec plus que de simples tableaux.

0
Xavier