C# 交换 List<T> 中的两项

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2094239/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 23:27:07  来源:igfitidea点击:

Swap two items in List<T>

c#linqswap

提问by Tony The Lion

Is there a LINQ way to swap the position of two items inside a list<T>?

是否有 LINQ 方法来交换 a 中两个项目的位置list<T>

采纳答案by Jan Jongboom

Check the answer from Marc from C#: Good/best implementation of Swap method.

检查来自C# 的Marc 的答案:交换方法的良好/最佳实现

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

which can be linq-i-fied like

可以像 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);

回答by jason

Maybe someone will think of a clever way to do this, but you shouldn't. Swapping two items in a list is inherently side-effect laden but LINQ operations should be side-effect free. Thus, just use a simple extension method:

也许有人会想到一个聪明的方法来做到这一点,但你不应该这样做。交换列表中的两个项目本质上是有副作用的,但 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;
    }
}

回答by CaffGeek

If order matters, you should keep a property on the "T" objects in your list that denotes sequence. In order to swap them, just swap the value of that property, and then use that in the .Sort(comparison with sequence property)

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

回答by Martin Mulder

There is no existing Swap-method, so you have to create one yourself. Of course you can linqify it, but that has to be done with one (unwritten?) rules in mind: LINQ-operations do not change the input parameters!

没有现有的 Swap 方法,因此您必须自己创建一个。当然,您可以对其进行 linqify,但这必须牢记一个(未成文的?)规则:LINQ 操作不会更改输入参数!

In the other "linqify" answers, the (input) list is modified and returned, but this action brakes that rule. If would be weird if you have a list with unsorted items, do a LINQ "OrderBy"-operation and than discover that the input list is also sorted (just like the result). This is not allowed to happen!

在其他“linqify”答案中,(输入)列表被修改并返回,但此操作会破坏该规则。如果您有一个包含未排序项目的列表会很奇怪,请执行 LINQ "OrderBy" 操作,然后发现输入列表也已排序(就像结果一样)。这是不允许发生的!

So... how do we do this?

那么……我们该怎么做呢?

My first thought was just to restore the collection after it was finished iterating. But this is a dirtysolution, so do not use it:

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

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

This solution is dirty because it doesmodify the input list, even if it restores it to the original state. This could cause several problems:

这个解决方案很脏,因为它确实修改了输入列表,即使它将它恢复到原始状态。这可能会导致几个问题:

  1. The list could be readonly which will throw an exception.
  2. If the list is shared by multiple threads, the list will change for the other threads during the duration of this function.
  3. If an exception occurs during the iteration, the list will not be restored. (This could be resolved to write an try-finally inside the Swap-function, and put the restore-code inside the finally-block).
  1. 该列表可以是只读的,这将引发异常。
  2. 如果列表由多个线程共享,则在此函数的持续时间内,其他线程的列表将更改。
  3. 如果在迭代过程中发生异常,则不会恢复列表。(这可以解决在 Swap 函数中写一个 try-finally,并将 restore-code 放在 finally-block 中)。

There is a better (and shorter) solution: just make a copy of the original list. (This also makes it possible to use an IEnumerable as a parameter, instead of an IList):

有一个更好(也更短)的解决方案:只需复制原始列表即可。(这也使得可以使用 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;
}

One disadvantage of this solution is that it copies the entire list which will consume memory and that makes the solution rather slow.

这种解决方案的一个缺点是它复制了整个列表,这会消耗内存,这使得解决方案相当慢。

You might consider the following solution:

您可以考虑以下解决方案:

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

And if you want to input parameter to be IEnumerable:

如果您想输入参数为 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 also makes a copy of (a subset of) the source. So worst case scenario, it is as slow and memory consuming as function Swap2.

Swap4 还制作源(的子集)的副本。所以最坏的情况是,它与函数 Swap2 一样慢且消耗内存。

回答by user1920925

List has Reverse method.

List 有 Reverse 方法。

your_list.Reverse(i, 2) // will swap elements with indexs i, i + 1. 

Source: https://msdn.microsoft.com/en-us/library/hf2ay11y(v=vs.110).aspx

来源:https: //msdn.microsoft.com/en-us/library/hf2ay11y(v=vs.110).aspx