2

假设我有一个列表:

IList<int> originalList = new List<int>();
originalList.add(1);
originalList.add(5);
originalList.add(10);

还有一个清单...

IList<int> newList = new List<int>();
newList.add(1);
newList.add(5);
newList.add(7);  
newList.add(11);

如何更新 originalList 以便:

  1. 如果 int 出现在 newList 中,保持
  2. 如果 int 没有出现在 newList 中,删除
  3. 将 newList 中的任何整数添加到 originalList 中尚不存在

因此 - 制作 originalList 的内容:

{ 1, 5, 7, 11 }

我问的原因是因为我有一个带有一组孩子的对象。当用户更新这个集合时,而不是仅仅删除所有孩子,然后插入他们的选择,我认为如果我只对添加或删除的孩子采取行动,而不是拆除整个集合,然后插入newList children 就好像他们都是新的一样。

编辑-对不起-我写了一个可怕的标题...我应该写“最少的代码”而不是“高效”。我认为这抛弃了我得到的很多答案。他们都很棒……谢谢!

4

10 回答 10

5
originalList = newList;

或者,如果您更喜欢它们是不同的列表:

originalList = new List<int>(newList);

但是,无论哪种方式都可以满足您的需求。根据您的规则,更新后,originalList 将与 newList 相同。

更新:感谢大家对这个答案的支持,但是在仔细阅读了这个问题之后,我相信我的其他回答(如下)是正确的。

于 2008-09-29T13:41:54.287 回答
2

如果你使用一些 LINQ 扩展方法,你可以分两行来做:

originalList.RemoveAll(x => !newList.Contains(x));
originalList.AddRange(newList.Where(x => !originalList.Contains(x)));

这假设(与其他人的解决方案一样)您已经覆盖了原始对象中的 Equals 。但是如果由于某种原因你不能覆盖 Equals,你可以像这样创建一个 IEqualityOperator:

class EqualThingTester : IEqualityComparer<Thing>
{
    public bool Equals(Thing x, Thing y)
    {
        return x.ParentID.Equals(y.ParentID);
    }

    public int GetHashCode(Thing obj)
    {
        return obj.ParentID.GetHashCode();
    }
}

然后上面的行变成:

originalList.RemoveAll(x => !newList.Contains(x, new EqualThingTester()));
originalList.AddRange(newList.Where(x => !originalList.Contains(x, new EqualThingTester())));

而且,如果您无论如何要传递 IEqualityOperator,则可以使第二行更短:

originalList.RemoveAll(x => !newList.Contains(x, new EqualThingTester()));
originalList.AddRange(newList.Except(originalList, new EqualThingTester()));
于 2008-09-29T16:01:56.663 回答
1

对不起,在我看到你的最后一段之前写了我的第一个回复。

for(int i = originalList.length-1; i >=0; --i)
{
     if (!newList.Contains(originalList[i])
            originalList.RemoveAt(i);
}

foreach(int n in newList)
{
     if (!originaList.Contains(n))
           originalList.Add(n);
}
于 2008-09-29T13:49:16.937 回答
1

如果您不担心最终的排序,Hashtable/HashSet 可能是最快的。

于 2008-09-29T13:50:00.147 回答
1

LINQ解决方案:

originalList = new List<int>(
                      from x in newList
                      join y in originalList on x equals y into z
                      from y in z.DefaultIfEmpty()
                      select x);
于 2008-09-29T13:50:41.423 回答
0

我最初的想法是你可以调用 originalList.AddRange(newList) 然后删除重复项 - 但我不确定这是否比清除列表并重新填充它更有效。

于 2008-09-29T13:44:08.057 回答
0
List<int> firstList = new List<int>() {1, 2, 3, 4, 5};
List<int> secondList = new List<int>() {1, 3, 5, 7, 9};

List<int> newList = new List<int>();

foreach (int i in firstList)
{
  newList.Add(i);
}

foreach (int i in secondList)
{
  if (!newList.Contains(i))
  {
    newList.Add(i);
  }
}

不是很干净——但它有效。

于 2008-09-29T13:48:04.177 回答
0

没有内置的方法可以做到这一点,我能想到的最接近的是 DataTable 处理新项目和已删除项目的方式。

@James Curran建议的只是将 originalList 对象替换为 newList 对象。它将转储oldList,但保留变量(即指针仍然存在)。

无论如何,您应该考虑优化这个时间是否值得。将值从一个列表复制到下一个列表所花费的大部分运行时间是否值得。如果不是,而是您正在做一些过早的优化,您应该忽略它。

在开始优化之前花时间完善 GUI 或分析应用程序是我的 0.02 美元。

于 2008-09-29T13:51:20.410 回答
0

这是开发人员在编写 UI 以维护多对多数据库关系时遇到的常见问题。我不知道这有多有效,但我写了一个帮助类来处理这种情况:

public class IEnumerableDiff<T>
{
    private delegate bool Compare(T x, T y);

    private List<T> _inXAndY;
    private List<T> _inXNotY;
    private List<T> _InYNotX;

    /// <summary>
    /// Compare two IEnumerables.
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="compareKeys">True to compare objects by their keys using Data.GetObjectKey(); false to use object.Equals comparison.</param>
    public IEnumerableDiff(IEnumerable<T> x, IEnumerable<T> y, bool compareKeys)
    {
        _inXAndY = new List<T>();
        _inXNotY = new List<T>();
        _InYNotX = new List<T>();
        Compare comparer = null;
        bool hit = false;

        if (compareKeys)
        {
            comparer = CompareKeyEquality;
        }
        else
        {
            comparer = CompareObjectEquality;
        }


        foreach (T xItem in x)
        {
            hit = false;
            foreach (T yItem in y)
            {
                if (comparer(xItem, yItem))
                {
                    _inXAndY.Add(xItem);
                    hit = true;
                    break;
                }
            }
            if (!hit)
            {
                _inXNotY.Add(xItem);
            }
        }

        foreach (T yItem in y)
        {
            hit = false;
            foreach (T xItem in x)
            {
                if (comparer(yItem, xItem))
                {
                    hit = true;
                    break;
                }
            }
            if (!hit)
            {
                _InYNotX.Add(yItem);
            }
        }
    }

    /// <summary>
    /// Adds and removes items from the x (current) list so that the contents match the y (new) list.
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="compareKeys"></param>
    public static void SyncXList(IList<T> x, IList<T> y, bool compareKeys)
    {
        var diff = new IEnumerableDiff<T>(x, y, compareKeys);
        foreach (T item in diff.InXNotY)
        {
            x.Remove(item);
        }
        foreach (T item in diff.InYNotX)
        {
            x.Add(item);
        }
    }

    public IList<T> InXAndY
    {
        get { return _inXAndY; }
    }

    public IList<T> InXNotY
    {
        get { return _inXNotY; }
    }

    public IList<T> InYNotX
    {
        get { return _InYNotX; }
    }

    public bool ContainSameItems
    {
        get { return _inXNotY.Count == 0 && _InYNotX.Count == 0; }
    }

    private bool CompareObjectEquality(T x, T y)
    {
        return x.Equals(y);
    }

    private bool CompareKeyEquality(T x, T y)
    {
        object xKey = Data.GetObjectKey(x);
        object yKey = Data.GetObjectKey(y);
        return xKey.Equals(yKey);
    }

}
于 2008-09-29T14:11:00.720 回答
0

如果您使用 .Net 3.5

var List3 = List1.Intersect(List2);

创建一个包含两个列表交集的新列表,我相信这就是您要在这里拍摄的。

于 2008-09-29T17:17:13.300 回答