Получение списка объектов, которые дважды повторяются в списке

У меня есть List<CustomPoint> points;, который содержит около миллиона объектов. Из этого списка я хотел бы получить список объектов, которые происходят ровно в два раза. Какой был бы самый быстрый способ сделать это? Я также был бы заинтересован в опции не Linq, так как мне, возможно, придется это сделать и на С++.

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(); 

но это дает нулевые объекты в новом списке. Может ли кто-нибудь помочь мне в этом?

Ответ 1

Чтобы заставить ваш код работать, вам нужно передать экземпляр вашего PointComparer в качестве второго аргумента в GroupBy.

Ответ 2

Вы должны реализовать Equals и GetHashCode в самом классе, а не в PointComparer

Ответ 3

Этот метод работает для меня:

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 я возвращаю CustomPoint в словаре, где PointCount.Count == count, в вашем случае 2.

Также обратите внимание, что я обновил метод GetHashCode(), так как ваш возвращает то же самое для точек (1,2) и (2,1). Если вы этого хотите, не стесняйтесь восстановить свой собственный метод хеширования. Вы должны будете проверить функцию хеширования, хотя, потому что трудно однозначно хешировать два числа в одном. Это зависит от диапазона используемых номеров, поэтому вы должны реализовать хэш-функцию, которая соответствует вашим собственным потребностям.