Скорость лямбда-выражения C#

Я не использовал много лямбда-выражений прежде, и я столкнулся со случаем, где я думал, что мог сделать гладкое использование из одного. У меня есть пользовательский список ~19 000 записей, и я должен узнать, существует ли запись или не в списке так вместо того, чтобы писать набор циклов, или использовать linq для прохождения через списка я решил попробовать это:

for (int i = MinX; i <= MaxX; ++i)
{
    tempY = MinY;

    while (tempY <= MaxY)
    {
        bool exists = myList.Exists(item => item.XCoord == i && item.YCoord == tempY);

        ++tempY;
    }
}

Только проблема - это, занимают ~9 - 11 секунд для выполнения. Я делаю, что-то не так - это просто случай того, где я не должен использовать выражение как это?

Спасибо.

Править: Прошу прощения. Я должен уточнить. Я создаю список записей с для и цикл с условием продолжения и проверяю, существует ли та запись в myList. Это - единственный способ, которым я мог думать о движении об этом. Я буду переоценивать его и видеть то, с чем я иду.

8
задан Mike Two 9 March 2010 в 14:13
поделиться

5 ответов

Ваш алгоритм неэффективен. Если вы выполняете несколько поисков в одном списке, вам необходимо:

  1. Отсортировать список соответствующим образом (по вашему ключу поиска).
  2. Используйте двоичный поиск , чтобы найти нужную запись.

Другой вариант, если память не является проблемой и вы хотите, чтобы она работала очень быстро, - это поместить записи в Dictionary . Это даст вам самый быстрый доступ после того, как вы его настроите.

6
ответ дан 5 December 2019 в 05:56
поделиться

Я собираюсь дополнить ответ Кевина красивым решением на основе linq.

Исходный код эффективно вычислял двумерный логический массив, указывающий, существует ли координата в myList в координатах x и y для каждого элемента массива.

Тест, используемый для каждого x и y , может быть выражен как лямбда-функция как таковая:

Func<int, int, bool> original =
    (x, y) =>
        myList.Exists(item => item.XCoord == x && item.YCoord == y);

Это неэффективно, поскольку метод Exists является вызывается, и, следовательно, список повторяется для каждой проверенной координаты x & y . Будет ли перебираться весь список или нет, зависит от того, найдено совпадение или нет. Во многих случаях совпадения не будет, поэтому весь список просматривается несколько раз.

Поэтому лучше предварительно вычислить словарь словарей, чтобы определить, существует ли координата в myList для любой координаты x и y .

var xyLookup =
    (from item in myList
     group item by item.XCoord into XCoords
     select new
     {
         X = XCoords.Key,
         YLookup = (from x in XCoords
                    group x by x.YCoord into YCoords
                    select new
                    {
                        Y = YCoords.Key,
                        Coords = YCoords
                    }).ToDictionary(a => a.Y, a => a.Coords)
     }).ToDictionary(b => b.X, b => b.YLookup);

xyLookup теперь позволяет следующей лямбда-функции заменить исходную версию.

Func<int, int, bool> refactored =
    (x, y) =>
        xyLookup.ContainsKey(x) && xyLookup[x].ContainsKey(y);

Предварительное вычисление xyLookup занимает некоторое время, поэтому, согласно моим тестам, если у меня есть массив 3x3 и myList содержит 3 координаты, то оба метода имеют примерно одинаковую скорость. Однако я ожидал, что фактический размер массива и количество элементов в myList на практике будут намного больше.

Если у меня есть массив 100x100 с 10 000 координат в myList , то xyLookup примерно в 91 раз быстрее, чем исходный метод.

Я люблю linq ...: -)

1
ответ дан 5 December 2019 в 05:56
поделиться

Этот код не имеет смысла для меня, поэтому он действительно сложно сказать, делаете ли вы это неправильно или нет. Велика вероятность, что да, вы делаете это неправильно.

Вместо того, чтобы показывать код, попробуйте это. Предположим, у вас есть метод, который делает в точности то, что вы хотите. Какой будет подпись этого метода? Не тело, а только подпись.

Предположим, например, вы хотите задать вопрос «содержит ли этот список точек конкретную точку?» Тогда подпись будет

bool Contains(List<Point> list, Point p)

Предположим, вы хотите задать вопрос «содержит ли этот список точек любую точку внутри этого прямоугольника?» Тогда подпись будет

bool ContainsAny(List<Point> list, Rectangle r)

. Предположим, вы хотите задать вопрос: «Что общего в этих двух списках?» тогда подпись будет

List<Point> Intersection(List<Point> l1, List<Point> l2)

и так далее. Более четко укажите, что вы пытаетесь сделать, и мы поможем вам оптимизировать эту операцию. Начнем с подписи.

12
ответ дан 5 December 2019 в 05:56
поделиться

Разве это не то, что вы хотите?

myList.Where(
    item=>item.XCoord>=MinX
        &&item.XCoord<=MaxX
        &&item.YCoord>=MinY
        &&item.YCoord<=MaxY
)

Все результирующие элементы будут удовлетворять критерию существует.

...или я неправильно понял?

2
ответ дан 5 December 2019 в 05:56
поделиться

Проблема не в лямбде, а в вашем алгоритме. Вы переходите от MinX к MaxX, сколько? затем вы проходите то же самое от MinY до MaxY, затем вы перебираете ~ 19 000 записей. поэтому, если цикл X равен 10, а цикл y равен 10, тогда вы делаете 19 000 * 10 * 10 вызовов. Могло быть намного хуже.

7
ответ дан 5 December 2019 в 05:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: