在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列表,即使它将其恢复到原始状态。 这可能会导致几个问题:
- 该列表可以只读,这将引发exception。
- 如果列表由多个线程共享,则在此函数的持续时间内,列表将在其他线程中更改。
- 如果在迭代期间发生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中使用它( 与sequence属性进行比较 )
列表有相反的方法。 your_list.Reverse(i,2)将交换索引为i,i + 1的元素。https://msdn.microsoft.com/zh-cn/library/hf2ay11y(v=vs.110).aspx