在List <T>中交换两个项目

有一个LINQ的方式来交换list<T>的两个项目的位置list<T>

从C#中检查Marc的答案:Swap方法的良好/最佳实现 。

 public static void Swap<T>(IList<T> list, int indexA, int indexB) { T tmp = list[indexA]; list[indexA] = list[indexB]; list[indexB] = tmp; } 

可以像linq-i-fied一样

 public static IList<T> Swap<T>(this IList<T> list, int indexA, int indexB) { T tmp = list[indexA]; list[indexA] = list[indexB]; list[indexB] = tmp; return list; } 

 var lst = new List<int>() { 8, 3, 2, 4 }; lst = lst.Swap(1, 2); 

也许有人会想到一个聪明的方法来做到这一点,但你不应该这样做。 在列表中交换两个项目本质上是副作用,但LINQ操作应该是无副作用的。 因此,只需使用一个简单的扩展方法:

 static class IListExtensions { public static void Swap<T>( this IList<T> list, int firstIndex, int secondIndex ) { Contract.Requires(list != null); Contract.Requires(firstIndex >= 0 && firstIndex < list.Count); Contract.Requires(secondIndex >= 0 && secondIndex < list.Count); if (firstIndex == secondIndex) { return; } T temp = list[firstIndex]; list[firstIndex] = list[secondIndex]; list[secondIndex] = temp; } } 

没有现有的Swap方法,所以你必须自己创build一个。 当然,你可以把它转换成LINQ操作,但是必须用一个(不成文的)规则来完成:LINQ操作不会改变input参数!

在其他的“linqify”答案中,(input)列表被修改并返回,但是这个动作刹住了这个规则。 如果你有一个没有sorting项目的列表,会出现奇怪的情况,请执行LINQ“OrderBy”操作,并发现input列表也被sorting(就像结果一样)。 这是不允许发生的!

那么…我们怎么做呢?

我的第一个想法是在迭代完成后恢复集合。 但这是一个肮脏的解决scheme,所以不要使用它:

 static public IEnumerable<T> Swap1<T>(this IList<T> source, int index1, int index2) { // Parameter checking is skipped in this example. // Swap the items. T temp = source[index1]; source[index1] = source[index2]; source[index2] = temp; // Return the items in the new order. foreach (T item in source) yield return item; // Restore the collection. source[index2] = source[index1]; source[index1] = temp; } 

此解决scheme很脏,因为它修改input列表,即使它将其恢复到原始状态。 这可能会导致几个问题:

  1. 该列表可以只读,这将引发exception。
  2. 如果列表由多个线程共享,则在此函数的持续时间内,列表将在其他线程中更改。
  3. 如果在迭代期间发生exception,列表将不会被恢复。 (这可以通过在Swap函数内部写一个try-finally来解决,并把restore-code放在finally块中)。

有一个更好的(和更短的)解决scheme:只需复制原始列表。 (这也可以使用IEnumerable作为参数,而不是IList):

 static public IEnumerable<T> Swap2<T>(this IList<T> source, int index1, int index2) { // Parameter checking is skipped in this example. // If nothing needs to be swapped, just return the original collection. if (index1 == index2) return source; // Make a copy. List<T> copy = source.ToList(); // Swap the items. T temp = copy[index1]; copy[index1] = copy[index2]; copy[index2] = temp; // Return the copy with the swapped items. return copy; } 

这个解决scheme的一个缺点是它复制整个列表,这将消耗内存,并使解决scheme相当缓慢。

您可以考虑以下解决scheme:

 static public IEnumerable<T> Swap3<T>(this IList<T> source, int index1, int index2) { // Parameter checking is skipped in this example. // It is assumed that index1 < index2. Otherwise a check should be build in and both indexes should be swapped. using (IEnumerator<T> e = source.GetEnumerator()) { // Iterate to the first index. for (int i = 0; i < index1; i++) yield return source[i]; // Return the item at the second index. yield return source[index2]; if (index1 != index2) { // Return the items between the first and second index. for (int i = index1 + 1; i < index2; i++) yield return source[i]; // Return the item at the first index. yield return source[index1]; } // Return the remaining items. for (int i = index2 + 1; i < source.Count; i++) yield return source[i]; } } 

如果你想input参数为IEnumerable:

 static public IEnumerable<T> Swap4<T>(this IEnumerable<T> source, int index1, int index2) { // Parameter checking is skipped in this example. // It is assumed that index1 < index2. Otherwise a check should be build in and both indexes should be swapped. using(IEnumerator<T> e = source.GetEnumerator()) { // Iterate to the first index. for(int i = 0; i < index1; i++) { if (!e.MoveNext()) yield break; yield return e.Current; } if (index1 != index2) { // Remember the item at the first position. if (!e.MoveNext()) yield break; T rememberedItem = e.Current; // Store the items between the first and second index in a temporary list. List<T> subset = new List<T>(index2 - index1 - 1); for (int i = index1 + 1; i < index2; i++) { if (!e.MoveNext()) break; subset.Add(e.Current); } // Return the item at the second index. if (e.MoveNext()) yield return e.Current; // Return the items in the subset. foreach (T item in subset) yield return item; // Return the first (remembered) item. yield return rememberedItem; } // Return the remaining items in the list. while (e.MoveNext()) yield return e.Current; } } 

Swap4还制作了一个(的子集)的副本。 所以最糟糕的情况是,它和Swap2一样慢,内存消耗。

如果订单很重要,则应该在列表中的“T”对象上保留一个属性,表示序列。 为了交换它们,只需交换该属性的值,然后在.Sort中使用它( 与sequ​​ence属性进行比较

列表有相反的方法。 your_list.Reverse(i,2)将交换索引为i,i + 1的元素。https://msdn.microsoft.com/zh-cn/library/hf2ay11y(v=vs.110).aspx