繁体   English   中英

协调3个列表的最佳算法

[英]best algorithm to reconcile 3 lists

我正在寻找一种方法来协调来自3个不同来源的元素。 我已将元素简化为只有一个键(字符串)和版本(长)。

这些列表是同时获得的(2个来自不同的数据库查询,1个来自另一个系统上的内存缓存)。

对于我的最终结果,我只关心所有3个来源中不是相同版本的元素。 所以我关心的结果将是一个键列表,每个系统中都有相应的版本。

Element1 | system1:v100    | system2:v100 | system3:v101 |
Element2 | system1:missing | system2:v200 | system3:v200 |

并且可以丢弃具有相同版本的元素。

我想到的实现这两种方法是

  1. 等待所有数据源完成检索,然后遍历每个列表以聚合具有密钥组合+所有3个版本的主列表(丢弃所有相同的项目)。

  2. 一旦检索完第一个列表,就将其放入并发集合(如字典(在.net 4.0中提供))中,并在可用时立即开始聚合剩余列表(并入集合)。

我的想法是第二种方法会更快一些,但可能不会太多。 在所有3个来源都存在之前,我真的不能做太多,所以从第二种方法中获得的并不多,并且引入了争用。

也许还有其他方法可以解决这个问题? 此外,由于版本是使用longs存储的,并且将有100个(可能是数百万)个元素,因此内存分配可能会受到关注(因为这些对象很短暂,所以可能不是一个大问题)

HashSet是一个选项,因为它具有Union和Intersect方法

HashSet.UnionWith方法

要使用它,您必须覆盖Equals和GetHashCode。
良好(唯一)哈希是性能的关键。

如果版本全部是v然后是数字,则可以使用数字来构建缺少为0的哈希。
让Int32玩,所以如果版本是Int10或更少可以创建一个完美的哈希。

另一个选项是ConcurrentDictionary(没有并发的HashSet)并且所有三个都加入了它。
仍然需要重写Equals和GetHashCode。
我的直觉是三个HashSets然后Union会更快。

如果所有版本都是数字版本,并且您可以使用0表示缺失,则可以将其打包到UInt32或UInt64中,并将其直接放在HashSet中。 联盟然后打开包装。 使用位推<<而不是数学来打包解包。

这只是两个UInt16但它在2秒内运行。
这比Hashing类要快。

如果所有三个版本都很长,那么HashSet <integral type>将不是一个选项。
long1 ^ long2 ^ long3; 可能是一个很好的哈希,但这不是我的专业知识。
我知道元组上的GetHashCode很糟糕。

class Program
{
    static void Main(string[] args)
    {
        HashSetComposite hsc1 = new HashSetComposite();
        HashSetComposite hsc2 = new HashSetComposite();
        for (UInt16 i = 0; i < 100; i++)
        {
            for (UInt16 j = 0; j < 40000; j++)
            {
                hsc1.Add(i, j);
            }
            for (UInt16 j = 20000; j < 60000; j++)
            {
                hsc2.Add(i, j);
            }
        }
        Console.WriteLine(hsc1.Intersect(hsc2).Count().ToString());
        Console.WriteLine(hsc1.Union(hsc2).Count().ToString());
    }
}

public class HashSetComposite : HashSet<UInt32>
{
    public void Add(UInt16 u1, UInt16 u2)
    {      
        UInt32 unsignedKey = (((UInt32)u1) << 16) | u2;
        Add(unsignedKey);           
    }
    //left over notes from long
    //ulong unsignedKey = (long) key;
    //uint lowBits = (uint) (unsignedKey & 0xffffffffUL);
    //uint highBits = (uint) (unsignedKey >> 32);
    //int i1 = (int) highBits;
    //int i2 = (int) lowBits;
}

使用ConcurrentDictionary测试,上面的速度提高了两倍。
锁上插件是很昂贵的。

您的问题似乎适合基于事件的解决方案。 基本上为每个来源分配事件以完成数据。 使用类型保持全局并发哈希。 在您的事件处理程序中查看已完成的数据源,如果您的并发哈希包含当前元素的键,则只需将其添加到列表中(如果不是仅插入具有给定元素的新列表)。

但是,根据您的性能要求,这可能会使您的应用程序过于复杂。 您的第一种方法是最简单的方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM