Я не использовал много лямбда-выражений прежде, и я столкнулся со случаем, где я думал, что мог сделать гладкое использование из одного. У меня есть пользовательский список ~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. Это - единственный способ, которым я мог думать о движении об этом. Я буду переоценивать его и видеть то, с чем я иду.
Ваш алгоритм неэффективен. Если вы выполняете несколько поисков в одном списке, вам необходимо:
Другой вариант, если память не является проблемой и вы хотите, чтобы она работала очень быстро, - это поместить записи в Dictionary
. Это даст вам самый быстрый доступ после того, как вы его настроите.
Я собираюсь дополнить ответ Кевина красивым решением на основе 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 ...: -)
Этот код не имеет смысла для меня, поэтому он действительно сложно сказать, делаете ли вы это неправильно или нет. Велика вероятность, что да, вы делаете это неправильно.
Вместо того, чтобы показывать код, попробуйте это. Предположим, у вас есть метод, который делает в точности то, что вы хотите. Какой будет подпись этого метода? Не тело, а только подпись.
Предположим, например, вы хотите задать вопрос «содержит ли этот список точек конкретную точку?» Тогда подпись будет
bool Contains(List<Point> list, Point p)
Предположим, вы хотите задать вопрос «содержит ли этот список точек любую точку внутри этого прямоугольника?» Тогда подпись будет
bool ContainsAny(List<Point> list, Rectangle r)
. Предположим, вы хотите задать вопрос: «Что общего в этих двух списках?» тогда подпись будет
List<Point> Intersection(List<Point> l1, List<Point> l2)
и так далее. Более четко укажите, что вы пытаетесь сделать, и мы поможем вам оптимизировать эту операцию. Начнем с подписи.
Разве это не то, что вы хотите?
myList.Where(
item=>item.XCoord>=MinX
&&item.XCoord<=MaxX
&&item.YCoord>=MinY
&&item.YCoord<=MaxY
)
Все результирующие элементы будут удовлетворять критерию существует
.
...или я неправильно понял?
Проблема не в лямбде, а в вашем алгоритме. Вы переходите от MinX к MaxX, сколько? затем вы проходите то же самое от MinY до MaxY, затем вы перебираете ~ 19 000 записей. поэтому, если цикл X равен 10, а цикл y равен 10, тогда вы делаете 19 000 * 10 * 10 вызовов. Могло быть намного хуже.