2009-04-03 8 views
58

Sto provando a confrontare due matrici l'una con l'altra. Ho provato questo codice e ho ottenuto i seguenti errori.Confronto di matrici in C#

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; 
} 

Perché ottengo quell'errore? Ho optato per una soluzione low-tech e ho fatto ciò che funziona, ma ho bisogno di copiarlo/incollarlo più volte per ogni tipo.

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; 
} 

risposta

71

"Perché ottengo quell'errore?" - Probabilmente, non si dispone di "using System.Collections;" nella parte superiore del file - solo "using System.Collections.Generic;" - tuttavia, i farmaci generici sono probabilmente più sicuro - vedi sotto:

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; 
} 
+1

Grazie per la seconda volta oggi Marc: D –

+1

questo dovrebbe funzionare solo per gli array ordinati. se non sono ordinati hai bisogno di almeno O (nlgn). – DarthVader

+6

@ user177883 Eh? Se i due array sono in ordine diverso, non dovrebbero essere considerati diversi? – RandomInsano

147

condizione che si disponga LINQ a disposizione e non fate cura troppo di prestazioni, la cosa più semplice è la seguente:

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

In realtà, è probabilmente la pena di verificare con riflettore o ILSpy ciò che i metodi SequenceEqual realmente fa, dal momento che potrebbero ottimizzare per le specifiche caso mai dei valori dell'array!

+13

Oppure solo a1.SequenceEquals (a2). Che cosa ti porta a credere che questo avrebbe comportato qualcosa di peggio della risposta accettata? –

+4

Suppongo che la versione enumerabile non possa fare affidamento sul confronto delle lunghezze, quindi la risposta accettata sarebbe più veloce nei casi in cui gli array confrontati erano di lunghezze diverse. –

+0

@Joel: Questa è essenzialmente la ragione. Il metodo SequenceEquals utilizza gli enumeratori, il che significa creare un'istanza di un iteratore e quindi effettuare numerose chiamate di funzione per eseguirne il ciclo, aggiungendo tutto il sovraccarico. Normalmente non ci penserei due volte prima di usare questo metodo - era solo un piccolo avvertimento. – Noldorin

2

SequenceEqual può essere più veloce. Vale a dire nel caso in cui quasi tutto il tempo, entrambi gli array hanno effettivamente la stessa lunghezza e non sono lo stesso oggetto.

Non ha ancora la stessa funzionalità della funzione OP, in quanto non confronta silenziosamente i valori nulli.

5

Recommending SequenceEqual è ok, ma pensare che potrebbe mai essere più veloce del solito per (;;) loop è troppo ingenuo.

ecco il codice riflessa:

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; 
} 

Come si può vedere che utilizza 2 enumeratori e spara numerose chiamate di metodo che ha gravemente lento tutto giù. Inoltre non controlla affatto la lunghezza, quindi nei casi brutti può essere ridicolmente più lento.

Confronta muovendo due iteratori con bella

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

e si sa che cosa voglio dire sulle prestazioni.

Può essere utilizzato nei casi in cui le prestazioni non sono davvero così importanti, forse nel codice di test unitario, o nei casi di una breve lista in metodi raramente chiamati.

+0

Ho leggermente modificato il tuo codice. Specifica la dichiarazione d'uso. Ho quasi fatto usare 'var' ma l'ho lasciato da solo –

12

Per NET 4.0 e più alto è possibile confrontare gli elementi in array o tuple via utilizzando StructuralComparisons Tipo:

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)); 
0

So che questo è un argomento vecchio, ma penso che sia ancora rilevante, e sarebbe piace condividere un'implementazione di un metodo di comparazione degli array che ritengo abbia il giusto equilibrio tra prestazioni ed eleganza.

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'idea è quella di verificare la presenza di tutte le prime condizioni per primo, poi ripiegare su SequenceEqual. Evita inoltre di eseguire branching extra e si basa invece su un cortocircuito booleano per evitare l'esecuzione non necessaria.Sento anche che sembra pulito ed è facile da capire.

Inoltre, utilizzando ICollection per i parametri, che possa funzionare con più appena array.

+0

Ora uso semplicemente SequenceEqual. –