2010-12-04 6 views
20

¿Cómo puedo almacenar pares de enteros en una lista? Sé que podría hacer una clase de ellos como:Almacenar un par de entradas en la lista

class Pair 
{ 
    int i1,i2; 
} 

Pero si hago lo que no soy capaz de utilizar la función Contains para comprobar si un determinado par está en la lista. ¿Cómo puedo hacer eso para poder almacenar fácilmente enteros en una lista y verificar si ya existe un par de enteros? No puedo usar la tabla porque no se sabe cuántos pares habrá.

EDIT:
olvidó añadir: En mis pares de programa (x, y) y (Y, X) son para ser tratados como iguales.

EDIT:
(x, y) y (y, x) son iguales al comprobar si Point está en la lista, pero x y y no se pueden intercambiar porque x y y representan una conexión entre dos puntos (el entero es la identificación del punto, y no, no puedo usar ninguna referencia, etc.). Cuando estoy comprobando si List contiene una conexión, no es importante si es (x, y) o (y, x) pero más tarde necesitaría esa información.

Respuesta

38

Si está utilizando .NET 4.0, se puede utilizar la clase Tuple como en

var tuple = new Tuple<int, int>(17, 42); 
var otherTuple = Tuple.Create(17, 42); 

y

var list = new List<Tuple<int, int>>(); 

Tenga en cuenta que si vas a la ruta de la utilización de Tuple<int, int> entonces necesitará para crear una implementación personalizada de IEqualityComparer<Tuple<TFirst, TSecond>> para reflejar sus reglas de igualdad que (x, y) se consideran igual a (y, x). A continuación, deberá pasar una instancia de este comparador al List<T>.Contains(T, IEqualityComparer<T>) (aquí T es Tuple<int, int> para usted).

class TupleAsUnorderedPairComparer : IEqualityComparer<Tuple<TFirst, TSecond>> { 
    public bool Equals(Tuple<TFirst, TSecond> x, Tuple<TFirst, TSecond> y) { 
     if(Object.ReferenceEquals(x, y)) { 
      return true; 
     } 
     if(x == null || y == null) { 
      return false; 
     } 
     return x.Item1 == y.Item1 && x.Item2 == y.Item2 || 
       x.Item1 == y.Item2 && x.Item2 == y.Item1; 
    } 

    public int GetHashCode(Tuple<TFirst, TSecond> x) { 
     if(x == null) { 
      return 0; 
     } 
     return x.Item1.GetHashCode()^x.Item2.GetHashCode(); 
    } 
} 

De lo contrario, si usted no puede o no desea utilizar Tuple, entonces tendrá que implementar un IEqualityComparer<Pair> para su clase Pair o anular Object.Equals y Object.GetHashCode.

class Pair { 
    public int First { get; private set; } 
    public int Second { get; private set; } 
    public Pair(int first, int second) { 
     this.First = first; 
     this.Second = second; 
    } 

    public override bool Equals(object obj) { 
     if(Object.ReferenceEquals(this, obj)) { 
      return true; 
     } 
     Pair instance = obj as Pair; 
     if(instance == null) { 
      return false; 
     } 
     return this.First == instance.First && this.Second == instance.Second || 
       this.First == instance.Second && this.Second == instance.First; 
    } 

    public override int GetHashCode() { 
     return this.First.GetHashCode()^this.Second.GetHashCode(); 
    } 
} 

y

class PairEqualityComparer : IEqualityComparer<Pair> { 
    // details elided 
} 

Si utiliza

list.Contains(pair); 

entonces se usará Equals y GetHashCode pero si se utiliza

list.Contains(pair, new PairEqualityComparer); 

entonces se usará PairEqualityComparer.Equals y PairEqualityComparer.GetHashCode. Tenga en cuenta que estos podrían ser ser diferentes a las implementaciones de Object.Equals y Object.GetHashCode.

Por último, si las pruebas de contención son algo que hará con frecuencia, entonces List no es su mejor opción; debe usar una clase diseñada para ese fin como HashSet.

+0

tengo edité mi respuesta para reflejar su necesidad de que '(x, y)' se considere igual a '(y, x)'. – jason

2

La clase es su mejor apuesta. Si está completamente inactivo usando el método Contains, deberá implementar la interfaz IComparable en su clase Pair. Esto le permitirá establecer qué significa "igualdad" para este par de números enteros.

La manera más simple sería crear la clase como usted tiene y luego crear y método de extensión en el objeto List<T>.

public static bool ContainsIntegers(this List<Pair> targetList, Pair comparer) { 
    foreach(Pair pair in targetList) 
    { 
     if(pair.i1 == comparer.i1 && pair.i2 == comparer.i2) return true; 
    } 
    return false; 
} 
1

Otra manera de hacer esto sería utilizar un List<ulong>, poblarlo poniendo el número más grande de los 32 bits y el otro número en los 32 bits más bajos:

ulong MakeEntry(int i1, int i2) 
{ 
    ulong hi = (ulong)Math.Max(i1, i2); 
    ulong lo = (ulong)Math.Min(i1, i2); 
    return (hi << 32) | lo; 
} 

List<ulong> items = new List<ulong>(); 

void DoSomething() 
{ 
    // get numbers i1 and i2 
    // and add to the list 
    items.Add(MakeEntry(i1, i2)); 

    // test to see if the pair is in the list 
    if (items.Contains(MakeEntry(i1, i2))) 
    { 
     // do whatever 
    } 
} 
+0

Esto no funciona debido al requisito de que '(x, y)' se trate como igual a '(y, x)'. Es decir, el OP usa pares _unordered_ en lugar de pares _ordered_. – jason

+0

@Jason: ¿Eh? El mínimo/máximo en el método 'MakeEntry' asegura que (x, y) y (y, x) sean tratados de manera idéntica. El más grande siempre está en los bits de orden superior. –

+0

Considerando que esta solución tratará (x, y) como igual a (y, x), lo hace intercambiando los dos valores. No tengo claro si el OP quiere que i1 e i2 sean accesibles individualmente. Si es así, esta solución no funcionaría. –

Cuestiones relacionadas