用一个列表更新一个列表所需的最少代码量是多少?
假设我有一个列表:
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以便:
- 如果int出现在newList中,请保留
- 如果int没有出现在newList中,则删除
- 将newList中尚未存在的所有int都添加到originalList中
从而使originalList的内容为:
{ 1, 5, 7, 11 }
我问的原因是因为我有一个带有子项集合的对象。当用户更新此集合时,我不只是删除所有子项,然后插入他们的选择,我认为,如果我对添加或者删除的子项采取行动,而不是拆除整个集合并插入子项,将会更有效率。 newList子级,好像它们都是新的一样。
编辑抱歉,我写了一个可怕的标题……我应该写的是"最少的代码"而不是"有效的"。我认为这摆脱了我所得到的很多答案。他们都很棒...谢谢!
解决方案
originalList = newList;
或者,如果我们希望它们成为不同的列表,请执行以下操作:
originalList = new List<int>(newList);
但是,无论哪种方式都可以满足需求。根据规则,更新后,originalList将与newList相同。
更新:我感谢大家对这个答案的支持,但是在仔细阅读了这个问题之后,我相信我的其他回答(如下)是正确的。
我最初的想法是,我们可以调用originalList.AddRange(newList)然后删除重复项,但是我不确定这是否比清除列表并重新填充更为有效。
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); } }
不是很干净-但是可以。
抱歉,在我们看到最后一段之前写了我的第一条回复。
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); }
如果我们不担心最终的排序,则Hashtable / HashSet可能是最快的。
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);
没有内置的方法可以执行此操作,我能想到的最接近的方法是DataTable处理新项目和已删除项目的方式。
@James Curran建议的只是用newList对象替换originalList对象。它将转储oldList,但保留变量(即,指针仍然存在)。
无论如何,我们都应该考虑优化是否花费了足够的时间。是将值从一个列表复制到下一个列表所花费的大部分运行时间,这可能是值得的。如果不是,而是正在做一些过早的优化,则应该忽略它。
在开始优化之前,花时间抛光GUI或者分析应用程序是我的$ .02.
这是开发人员在编写用于维护多对多数据库关系的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); } }
如果使用某些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()));
如果我们使用的是.Net 3.5
var List3 = List1.Intersect(List2);
创建一个包含两个列表的交集的新列表,我相信这是我们要在此处拍摄的内容。