2009-04-03 29 views
58

Estoy tratando de comparar dos matrices entre sí. Intenté este código y obtuve los siguientes errores.Comparación de matrices en 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; 
} 

¿Por qué me sale ese error? Fui por una solución de baja tecnología e hice esto que funciona bien, pero necesito copiarlo y pegarlo varias veces para cada 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; 
} 

Respuesta

71

"¿Por qué me sale el error?" - Probablemente, usted no tiene "using System.Collections;" en la parte superior del archivo - única "using System.Collections.Generic;" - sin embargo, los genéricos son probablemente más seguro - ver más abajo:

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

Gracias por la 2da vez hoy Marc: D –

+1

esto debería funcionar solo para arreglos ordenados. si no están ordenados, necesitas al menos O (nlgn). – DarthVader

+6

@ user177883 ¿Eh? Si las dos matrices están en orden diferente, ¿no deberían considerarse diferentes? – RandomInsano

147

Siempre que usted tiene LINQ disponible y no lo hace importa demasiado sobre el rendimiento, lo más fácil es la siguiente:

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

de hecho, es probable que vale la pena mirar con Reflector o ILSpy lo que los métodos SequenceEqual realmente hace, ya que bien pueden optimizar para la especificación ¡caso particular de valores de matriz de todos modos!

+13

O simplemente a1.SequenceEquals (a2). ¿Qué te lleva a creer que esto funcionaría peor que la respuesta aceptada? –

+4

Supongo que la versión enumerable no puede depender de la comparación de longitudes, por lo que la respuesta aceptada sería más rápida en los casos en que las matrices que se comparan son de diferentes longitudes. –

+0

@Joel: Esa es esencialmente la razón. El método SequenceEquals utiliza enumeradores, lo que significa crear instancias de un iterador y luego realizar numerosas llamadas a funciones para recorrerlo, todo lo cual agrega sobrecarga. Normalmente no lo pensaría dos veces antes de usar este método, era solo una pequeña advertencia. – Noldorin

2

SequenceEqual puede ser más rápido. A saber, en el caso donde casi todo el tiempo, ambas matrices tienen la misma longitud y no son el mismo objeto.

Todavía no es la misma funcionalidad que la función del OP, ya que no se compararán silenciosamente los valores nulos.

5

Recomendar SequenceEqual está bien, pero pensar que puede ser siempre más rápido que lo habitual para (;;) loop es demasiado ingenuo.

Aquí está el código reflejado:

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

Como se puede ver que utiliza 2 enumeradores y dispara numerosas llamadas de método, que seriamente lento todo abajo. Además, no comprueba la longitud en absoluto, por lo que en los casos malos puede ser ridículamente más lenta.

Compara mover dos iteradores con hermosa

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

y usted sabrá lo que quiero decir sobre el rendimiento.

Se puede usar en casos donde el rendimiento realmente no es tan crítico, tal vez en el código de prueba unitario, o en el caso de alguna lista breve en métodos raramente llamados.

+0

Modifiqué ligeramente su código. Específica la declaración de uso. Casi lo hice usar 'var' pero lo dejé solo –

12

Para .NET 4.0 y más alto se puede comparar elementos en serie o tuplas mediante el uso de 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

Sé que esto es un viejo tema, pero creo que sigue siendo relevante, y me me gusta compartir una implementación de un método de comparación de matriz que creo que logra el equilibrio correcto entre rendimiento y elegancia.

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

La idea aquí es para comprobar si todas las condiciones tempranas, en primer lugar, y luego caer de nuevo SequenceEqual. También evita hacer una bifurcación adicional y en su lugar se basa en un cortocircuito booleano para evitar una ejecución innecesaria.También siento que se ve limpio y es fácil de entender.

Además, al usar ICollection para los parámetros, funcionará con más que solo arreglos.

+0

Simplemente uso SequenceEqual ahora. –