2010-12-04 2 views
20

Как хранить пары целых чисел в списке? Я знаю, что я мог бы сделать класс для них, как:Сохранение пары int в списке

class Pair 
{ 
    int i1,i2; 
} 

Но если я, что я не в состоянии использовать функцию Contains, чтобы проверить, является ли данная пара находится в списке. Как я могу это сделать, чтобы я мог легко хранить целые числа в списке и проверить, существует ли пара целых чисел? Я не могу использовать таблицу, потому что неизвестно, сколько пар будет.

EDIT:
Забыл добавить: В моих парах программ (х, у) и (у, х), должны рассматриваться как равные.

РЕДАКТИРОВАТЬ:
(х, у) и (у, х) равны при проверке, если Point находится в списке, но x и y не может быть заменен, потому что x и y представляют собой связь между двумя точками (целое число равно id, и я не могу использовать ссылку и т. д.). Когда я проверяю, содержит ли List соединение, это не важно, если это (x, y) или (y, x), но позже мне понадобится эта информация.

ответ

38

Если вы используете .NET 4.0, можно использовать Tuple класс как в

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

и

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

Обратите внимание, что если вы идете маршрут, используя Tuple<int, int>, то вам нужно будет для создания пользовательской реализации IEqualityComparer<Tuple<TFirst, TSecond>>, чтобы отразить ваши правила равенства, которые (x, y) считаются равными (y, x). Затем вам необходимо передать экземпляр этого сравнения в List<T>.Contains(T, IEqualityComparer<T>) (здесь T - Tuple<int, int> для вас).

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

В противном случае, если вы не можете или не хотите использовать Tuple, то вам нужно будет реализовать IEqualityComparer<Pair> для Pair класса или переопределить Object.Equals и 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(); 
    } 
} 

и

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

Если вы используете

list.Contains(pair); 

тогда он будет использовать Equals и GetHashCode, но если вы используете

list.Contains(pair, new PairEqualityComparer); 

, то он будет использовать PairEqualityComparer.Equals и PairEqualityComparer.GetHashCode. Обратите внимание, что эти могут быть отличаться от ваших реализаций Object.Equals и Object.GetHashCode.

И, наконец, если тестирование на сдерживание - это то, что вы часто будете делать, то List - не лучший выбор; вы должны использовать класс, предназначенный для этой цели, например, HashSet.

+0

Я отредактировал мой ответ, чтобы отразить вашу потребность в `(x, y)`, чтобы считаться равным `(y, x)`. – jason 2010-12-04 17:00:29

2

Класс - ваш лучший выбор. Если вы настроены на использование метода Contains, вам необходимо реализовать интерфейс IComparable в вашем классе Pair. Это позволит вам установить, что означает «равенство» для этой пары целых чисел.

Простейшим способом было бы создать класс, который у вас есть, а затем создать и расширить метод на объекте 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

Другой способ сделать это было бы использовать List<ulong>, заселять его, поставив наибольшее число в верхних 32 бит, а другой номер в нижних 32 бит:

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

Это не работает из-за требования, чтобы `(x, y)` считалось равным `(y, x)`. То есть OP использует пары _unordered_ в отличие от пар _ordered_. – jason 2010-12-04 19:27:15

Смежные вопросы