问题 获取列表中两次出现exaclty的对象列表


我有一个 List<CustomPoint> points; 其中包含近百万个对象。 从这个列表中我想得到恰好两次出现的对象列表。最快的方法是什么?我也会对非Linq选项感兴趣,因为我可能也必须在C ++中这样做。

public class CustomPoint
{
    public double X { get; set; }
    public double Y { get; set; }

    public CustomPoint(double x, double y)
    {
        this.X = x;
        this.Y = y;
    }
}

public class PointComparer : IEqualityComparer<CustomPoint>
{
    public bool Equals(CustomPoint x, CustomPoint y)
    {
        return ((x.X == y.X) && (y.Y == x.Y));
    }

    public int GetHashCode(CustomPoint obj)
    {
        int hash = 0;
        hash ^= obj.X.GetHashCode();
        hash ^= obj.Y.GetHashCode();
        return hash;
    }
}

基于 这个 回答,我试过,

list.GroupBy(x => x).Where(x => x.Count() = 2).Select(x => x.Key).ToList(); 

但是这会在新列表中给出零对象。 有人可以指导我吗?


1015
2017-12-20 11:59


起源

你从哪里获得数据? - Mike Perrenoud
@MichaelPerrenoud--这些是地理纬度和经度值 - vinayan
是的,但是从哪里加载数据(例如SQL Server数据库)?我问,因为你提到它接近一百万个对象,而且你可能需要用C ++访问它 - Mike Perrenoud
@MichaelPerrenoud - 好的..我正在从shapefile中读取这些点(en.wikipedia.org/wiki/Shapefile)。我正在使用一个名为Quantum GIS及其C ++ API的应用程序。 - vinayan
你可能会或可能不会意识到这一点,但比较双打的平等通常不是一个好主意。看到 stackoverflow.com/questions/1398753/... - Sebastian Negraszus


答案:


要使代码正常工作,您需要传递一个实例 PointComparer 作为第二个论点 GroupBy


4
2017-12-20 12:01



我尝试了points.GroupBy(new PointComparer())。其中(g => g.Count()== 2).Select(g => g.Key).ToList();它没有编译 - vinayan
对不起,你需要两个lambda 和 该 new PointComparer() (两个参数)。 - Rawling
+1有效!让我等一下,看看是否存在任何非lambda方法。 - vinayan


您应该在类本身而不是PointComparer中实现Equals和GetHashCode


9
2017-12-20 12:02



我为点类实现了IEqualityComparer ..它没有工作..我必须做一些非常错误的事情 - vinayan
不,它没关系,但你可以用另一种方式,但如果你想使用equals和gethashcode方法,你必须在主类上使用它们,而不是在IEqualityComparer上使用它们 - Sawan


这个方法适合我:

public class PointCount
{
    public CustomPoint Point { get; set; }
    public int Count { get; set; }
}

private static IEnumerable<CustomPoint> GetPointsByCount(Dictionary<int, PointCount> pointcount, int count)
{
    return pointcount
                    .Where(p => p.Value.Count == count)
                    .Select(p => p.Value.Point);
}

private static Dictionary<int, PointCount> GetPointCount(List<CustomPoint> pointList)
{
    var allPoints = new Dictionary<int, PointCount>();

    foreach (var point in pointList)
    {
        int hash = point.GetHashCode();

        if (allPoints.ContainsKey(hash))
        {
            allPoints[hash].Count++;
        }
        else
        {
            allPoints.Add(hash, new PointCount { Point = point, Count = 1 });
        }
    }

    return allPoints;
}

这样称呼:

static void Main(string[] args)
{
    List<CustomPoint> list1 = CreateCustomPointList();

    var doubles = GetPointsByCount(GetPointCount(list1), 2);

    Console.WriteLine("Doubles:");
    foreach (var point in doubles)
    {
        Console.WriteLine("X: {0}, Y: {1}", point.X, point.Y);
    }
}

private static List<CustomPoint> CreateCustomPointList()
{
    var result = new List<CustomPoint>();

    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            result.Add(new CustomPoint(i, j));
        }
    }

    result.Add(new CustomPoint(1, 3));
    result.Add(new CustomPoint(3, 3));
    result.Add(new CustomPoint(0, 2));

    return result;
}

CustomPoint 执行:

public class CustomPoint
{
    public double X { get; set; }
    public double Y { get; set; }

    public CustomPoint(double x, double y)
    {
        this.X = x;
        this.Y = y;
    }

    public override bool Equals(object obj)
    {
        var other = obj as CustomPoint;

        if (other == null)
        {
            return base.Equals(obj);
        }

        return ((this.X == other.X) && (this.Y == other.Y));
    }

    public override int GetHashCode()
    {
        int hash = 23;
        hash = hash * 31 + this.X.GetHashCode();
        hash = hash * 31 + this.Y.GetHashCode();
        return hash;
    }
}

它打印:

Doubles:
X: 0, Y: 2
X: 1, Y: 3
X: 3, Y: 3

如你所见 GetPointCount(),我每个独特的创建一个字典 CustomPoint (通过哈希)。然后我插入一个 PointCount 包含对引用的对象 CustomPoint 从一开始 Count 为1,并且每次遇到相同的点, Count 增加了。

最后在 GetPointsByCount 我回来了 CustomPoints在字典里 PointCount.Count == count,在你的情况下2。

还请注意我更新了 GetHashCode() 方法,因为你的那个为点(1,2)和(2,1)返回相同的方法。如果您确实需要,请随意恢复自己的哈希方法。您将不得不测试散列函数,因为很难将两个数字唯一地散列为一个。这取决于使用的数字范围,因此您应该实现适合您自己需要的哈希函数。


3
2017-12-20 12:26



这与我的答案相同,但是使用代码,它不需要代码,他可以非常轻松地编写代码,然后是你的时间 - Sawan
@MSakherSawan “这与我的答案相同”  - 不,不是,我实施了 GetDoubles() 与你的答案相反的方法确实回答了这个问题。 - CodeCaster
@flindeberg这是真的,但你必须同步字典访问以防止并发问题。不知道你是否从中获益很多。 - CodeCaster
@CodeCaster我在更多地考虑map-reduce :)但它当然取决于原始数据的组成,如果有很多相似点map-reduce会真的有帮助,但如果没有它真的会不会“T。 - flindeberg
@vinayan CodeCaster的实现 GetHashCode  故意 避免这样做,因为通常这是一个坏主意;如果你 想 事实就是这样简单地取代他的 GetHashCode 与你的问题中的一个。 - Rawling