协调3个列表的最佳算法

时间:2012-10-18 12:50:55

标签: c# .net algorithm

我正在寻找一种方法来协调来自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. 我的想法是第二种方法会更快一点,但可能不会太多。在所有3个来源都存在之前,我真的不能做太多,所以从第二种方法中获得的并不多,并且引入了争用。

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

2 个答案:

答案 0 :(得分:2)

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

HashSet.UnionWith Method

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

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

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

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

这只是两个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测试,上面的速度提高了两倍 锁上插件是很昂贵的。

答案 1 :(得分:0)

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

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