C#2.0中两个IList同步的最佳算法

C#2.0中两个IList同步的最佳算法,c#,.net,algorithm,optimization,list,C#,.net,Algorithm,Optimization,List,想象一下以下类型: public struct Account { public int Id; public double Amount; } 在C#2.0中,同步两个IList的最佳算法是什么?(没有linq) 第一个列表(L1)是参考列表,第二个列表(L2)是根据第一个列表进行同步的列表: 必须从L2中删除L2中不再存在的所有帐户 必须更新L2中仍然存在于L1中的所有帐户(金额属性) 所有在L1中但尚未在L2中的帐户必须添加到L2 Id标识帐户。找到一个简单有效的算法

想象一下以下类型:

public struct Account
{
    public int Id;
    public double Amount;
}
在C#2.0中,同步两个IList的最佳算法是什么?(没有linq)

第一个列表(L1)是参考列表,第二个列表(L2)是根据第一个列表进行同步的列表:

  • 必须从L2中删除L2中不再存在的所有帐户
  • 必须更新L2中仍然存在于L1中的所有帐户(金额属性)
  • 所有在L1中但尚未在L2中的帐户必须添加到L2
Id标识帐户。找到一个简单有效的算法并不难,但我想知道是否有一个聪明的解决方案可以在不破坏可读性和性能的情况下处理这个场景

编辑

  • 帐户类型不重要,它可以是一个类、具有属性、相等成员等
  • L1和L2未排序
  • L2项不能被L1项替换,必须更新它们(逐字段、逐属性)

首先,我要去掉可变结构。可变值类型从根本上说是一件坏事。(国际海事组织的公共领域也是如此。)

可能值得建立一本词典,以便您可以轻松比较两个列表的内容。一旦你有了检查是否在场的简单方法,剩下的就应该很简单了


老实说,听起来你基本上希望二语是一个完整的一级语言的副本。。。清除L2,然后直接呼叫AddRange?或者,在更改L2时,您是否还想执行其他操作?

除了Jon Skeet的注释之外,还可以使您的帐户结构成为一个类,并重写Equals()和GetHashCode()方法,以获得良好的相等性检查。

L2=L1.clone()


。。。但是我猜你忘了提什么了。

如果你的两个列表是排序的,那么你可以简单地依次浏览它们。这是一个O(m+n)操作。以下代码可能会有所帮助:

class Program
{
    static void Main()
    {
        List<string> left = new List<string> { "Alice", "Charles", "Derek" };
        List<string> right = new List<string> { "Bob", "Charles", "Ernie" };

        EnumerableExtensions.CompareSortedCollections(left, right, StringComparer.CurrentCultureIgnoreCase,
            s => Console.WriteLine("Left: " + s), s => Console.WriteLine("Right: " + s), (x,y) => Console.WriteLine("Both: " + x + y));
    }
}

static class EnumerableExtensions
{
    public static void CompareSortedCollections<T>(IEnumerable<T> source, IEnumerable<T> destination, IComparer<T> comparer, Action<T> onLeftOnly, Action<T> onRightOnly, Action<T, T> onBoth)
    {
        EnumerableIterator<T> sourceIterator = new EnumerableIterator<T>(source);
        EnumerableIterator<T> destinationIterator = new EnumerableIterator<T>(destination);

        while (sourceIterator.HasCurrent && destinationIterator.HasCurrent)
        {
            // While LHS < RHS, the items in LHS aren't in RHS
            while (sourceIterator.HasCurrent && (comparer.Compare(sourceIterator.Current, destinationIterator.Current) < 0))
            {
                onLeftOnly(sourceIterator.Current);
                sourceIterator.MoveNext();
            }

            // While RHS < LHS, the items in RHS aren't in LHS
            while (sourceIterator.HasCurrent && destinationIterator.HasCurrent && (comparer.Compare(sourceIterator.Current, destinationIterator.Current) > 0))
            {
                onRightOnly(destinationIterator.Current);
                destinationIterator.MoveNext();
            }

            // While LHS==RHS, the items are in both
            while (sourceIterator.HasCurrent && destinationIterator.HasCurrent && (comparer.Compare(sourceIterator.Current, destinationIterator.Current) == 0))
            {
                onBoth(sourceIterator.Current, destinationIterator.Current);
                sourceIterator.MoveNext();
                destinationIterator.MoveNext();
            }
        }

        // Mop up.
        while (sourceIterator.HasCurrent)
        {
            onLeftOnly(sourceIterator.Current);
            sourceIterator.MoveNext();
        }

        while (destinationIterator.HasCurrent)
        {
            onRightOnly(destinationIterator.Current);
            destinationIterator.MoveNext();
        }
    }
}

internal class EnumerableIterator<T>
{
    private readonly IEnumerator<T> _enumerator;

    public EnumerableIterator(IEnumerable<T> enumerable)
    {
        _enumerator = enumerable.GetEnumerator();
        MoveNext();
    }

    public bool HasCurrent { get; private set; }

    public T Current
    {
        get { return _enumerator.Current; }
    }

    public void MoveNext()
    {
        HasCurrent = _enumerator.MoveNext();
    }
}
类程序
{
静态void Main()
{
左名单=新名单{“爱丽丝”、“查尔斯”、“德里克”};
列表右=新列表{“Bob”、“Charles”、“Ernie”};
EnumerableExtensions.CompareSortedCollections(左、右、StringComparer.CurrentCultureIgnoreCase、,
s=>Console.WriteLine(“左:”+s),s=>Console.WriteLine(“右:”+s),(x,y)=>Console.WriteLine(“两者:”+x+y));
}
}
静态类EnumerableExtensions
{
公共静态void CompareSortedCollections(IEnumerable源、IEnumerable目标、IComparer比较器、仅左操作、仅右操作、同时操作)
{
EnumerableTerator sourceIterator=新的EnumerableTerator(源);
EnumerableIterator DestinationItemerator=新的EnumerableIterator(目标);
while(sourceIterator.HasCurrent&&destinationIterator.HasCurrent)
{
//当LHS0))
{
onRightOnly(destinationiter.Current);
destinationiter.MoveNext();
}
//当LHS==RHS时,两个项目同时存在
而(sourceIterator.HasCurrent&&destinationIterator.HasCurrent&&comparer.Compare(sourceIterator.Current,destinationIterator.Current)==0))
{
onBoth(sourceIterator.Current、destinationIterator.Current);
sourceIterator.MoveNext();
destinationiter.MoveNext();
}
}
//扫荡。
while(sourceIterator.HasCurrent)
{
onLeftOnly(sourceIterator.Current);
sourceIterator.MoveNext();
}
while(destinationiter.HasCurrent)
{
onRightOnly(destinationiter.Current);
destinationiter.MoveNext();
}
}
}
内部类枚举计数器
{
私有只读IEnumerator\u枚举器;
公共枚举计数器(IEnumerable enumerable)
{
_enumerator=enumerable.GetEnumerator();
MoveNext();
}
public bool HasCurrent{get;private set;}
公共电流
{
获取{return}enumerator.Current;}
}
公共图书馆
{
HasCurrent=_枚举数。MoveNext();
}
}
不过,在对集合进行迭代时,必须小心修改集合

如果它们没有被排序,那么将一个元素中的每个元素与另一个元素中的每个元素进行比较就是O(mn),这会很快变得很痛苦


如果你能忍受将每个集合中的键值复制到字典或类似的集合中(即当被问到“X是否存在?”时性能可以接受的集合),那么你可以想出一些合理的方法。

我知道这是一篇老文章,但你应该查看AutoMapper。它将以非常灵活和可配置的方式完全满足您的需求


我也遇到了同样的问题,我的最佳解决方案是(根据您的情况进行调整)加载两个列表:

  • 对于L1中的每个帐户,验证其是否存在于L2中:
    • 如果找到,则根据L2更新L1帐户中的所有值。然后,从L2中删除该帐户
    • 如果未找到,则将L1的帐户标记为已删除,或从列表中删除,这取决于数据库的结构
  • 对于L2中剩余的每个帐户,将其添加到L1中
  • 我建议在Account类中实现
    IEquatable
    接口(或者只重写
    Equals()
    方法),以便它总是比较m上的ID
    public struct Account : IEquatable<Account>
    {
        public int Id;
        public double Amount;
    
        public bool Equals(Account other)
        {
            if (other == null) return false;
            return (this.Id.Equals(other.Id));
        }
    }
    
    L1.ForEach (L1Account =>
    {
        var L2Account = L2.Find(a => a.Id == L1Account.id);
        // If found, update values
        if (L2Account != null)
        {
            L1Account.Amount = L2Account.Amount;
            L2.Remove(L2Account);
        }
        // If not found, remove it
        else
        {
            L1.Remove(L1Account);
        }
    }
    // Add any remaining L2 Account to L1
    L1.AddRange(L2);