Как найти элемент списка в другом списке?

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

Я вижу два способа сделать это. Скажем, наши списки:

List<string> list1 = new[] { "A", "C", "F", "H", "I" };
List<string> list2 = new[] { "B", "D", "F", "G", "I" };

Первый подход использует цикл:

bool isFound = false;
foreach (item1 in list1)
{
    if (list2.Contains(item1))
    {
        isFound = true;
        break;
    }
}

Второй использует Linq напрямую:

bool isFound = list1.Intersect(list2).Any();

Первый из них длинный, чтобы писать, а не очень просто/легко читаться. Второй - короткий и понятный, но производительность будет низкой, особенно в больших списках.

Что может быть изящным способом сделать это?

Ответы

Ответ 1

Вторая имеет лучшую производительность в больших списках, чем первая. Intersect помещает элементы одного списка в хеш-таблицу перед проверкой других элементов списка для членства.

Ответ 2

Кажется странным критиковать производительность LINQ, когда оригинал явно (худший случай) O (n * m); подходом LINQ я ожидал бы использовать HashSet<T> в списке, а затем использовать блок потокового итератора, поэтому производительность должна быть O (n + m) - то есть лучше.

Ответ 3

Я думаю, что второй будет быстрее для больших списков. Поскольку первый из них - O (list1.Count * list2.Count), а второй - O (list1.Count + list2.Count). Во-вторых, занимает больше памяти.

И накладные расходы на linq обычно являются постоянным коэффициентом умножения по сравнению с кодом ручной работы. Я предполагаю, что второй медленнее, чем императивный код, по крайней мере, в два раза, возможно, даже не это. Он использует память O(list1.Count+list2.Count), которую можно сократить до O(Min(list1,list2)), если вы тщательно напишите свой код для использования с низкой памятью, сохраняя при этом линейную производительность.

Этот код должен быть относительно быстрым в больших списках:

bool isFound = false;
HashSet<string> set2=new HashSet<string>(list2);
foreach (item1 in list1)
{
    if (set2.Contains(item1))
    {
        isFound = true;
        break;
    }
}

Вы также можете оптимизировать этот код, сделав меньший список в хэш-набор, а не всегда используя list2.

Ответ 4

Принятый ответ велик, однако он не работает с Linq-to-sql, так как нет отображения для Intersect. В этом случае вы должны использовать:

bool isFound = table.Any(row => list2.Contains(row.FieldWithValue));

Это скомпилируется в WHERE EXSITS

Ответ 5

Это еще один способ узнать, существует ли элемент одного списка в другом списке.

bool present = List1.Any(t => List2.Any(y => y == t));