Эффективно объедините массивы строк в.NET, сохранив отличные значения

Указатель NULL - это тот, который указывает на никуда. Когда вы разыскиваете указатель p, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p является нулевым указателем, местоположение, хранящееся в p, является nowhere, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception.

В общем, это потому, что что-то не было правильно инициализировано.

39
задан Jason Anderson 14 September 2014 в 21:20
поделиться

6 ответов

string[] result = list1.Union(list2).ToArray();

от msdn: "Этот метод исключает дубликаты из набора возврата. Это - различное поведение к Concat (TSource) метод, который возвращает все элементы во входных последовательностях включая дубликаты".

95
ответ дан Highmastdon 27 November 2019 в 02:09
поделиться

Почему Вы предполагаете, что это было бы неэффективно? Насколько я знаю, и Concat и Distinct оценены лениво, с помощью HashSet негласно для Отличного для отслеживания элементы, которые были уже возвращены.

я не уверен, как Вам удалось бы сделать его более эффективным, чем это общим способом:)

РЕДАКТИРОВАНИЕ: Отличный на самом деле использует Набор (внутренний класс) вместо HashSet, но суть все еще корректна. Это - действительно хороший пример, как аккуратный LINQ. Самый простой ответ в значительной степени так эффективен, как можно достигнуть без более знаний проблемной области.

эффект является эквивалентом:

public static IEnumerable<T> DistinctConcat<T>(IEnumerable<T> first, IEnumerable<T> second)
{
    HashSet<T> returned = new HashSet<T>();
    foreach (T element in first)
    {
        if (returned.Add(element))
        {
            yield return element;
        }
    }
    foreach (T element in second)
    {
        if (returned.Add(element))
        {
            yield return element;
        }
    }
}
12
ответ дан Jon Skeet 27 November 2019 в 02:09
поделиться

. СЕТЕВЫЕ 3.5 представили класс HashSet, который мог сделать это:

IEnumerable<string> mergedDistinctList = new HashSet<string>(list1).Union(list2);

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

РЕДАКТИРОВАНИЕ: Я признаю ошибку. Ленивая реализация Concat и Distinct имеет ключевое преимущество памяти И скорости. Concat/Distinct приблизительно на 10% быстрее, и сохраняет несколько копий данных.

я подтвердил через код:

Setting up arrays of 3000000 strings overlapping by 300000
Starting Hashset...
HashSet: 00:00:02.8237616
Starting Concat/Distinct...
Concat/Distinct: 00:00:02.5629681

вывод:

        int num = 3000000;
        int num10Pct = (int)(num / 10);

        Console.WriteLine(String.Format("Setting up arrays of {0} strings overlapping by {1}", num, num10Pct));
        string[] list1 = Enumerable.Range(1, num).Select((a) => a.ToString()).ToArray();
        string[] list2 = Enumerable.Range(num - num10Pct, num + num10Pct).Select((a) => a.ToString()).ToArray();

        Console.WriteLine("Starting Hashset...");
        Stopwatch sw = new Stopwatch();
        sw.Start();
        string[] merged = new HashSet<string>(list1).Union(list2).ToArray();
        sw.Stop();
        Console.WriteLine("HashSet: " + sw.Elapsed);

        Console.WriteLine("Starting Concat/Distinct...");
        sw.Reset();
        sw.Start();
        string[] merged2 = list1.Concat(list2).Distinct().ToArray();
        sw.Stop();
        Console.WriteLine("Concat/Distinct: " + sw.Elapsed);
3
ответ дан TheSoftwareJedi 27 November 2019 в 02:09
поделиться

Правовая оговорка Это - преждевременная оптимизация. Для Ваших массивов в качестве примера используйте 3,5 дополнительных метода. Пока Вы не знаете, что у Вас есть проблема производительности в этом регионе, необходимо использовать код библиотеки.

<час>

, Если можно отсортировать массивы, или они отсортированы, когда Вы добираетесь до той точки в коде, можно использовать следующие методы.

Они вытянут один объект от обоих, и произведут "самый низкий" объект, затем выберут новый объект из соответствующего источника, пока оба источника не будут исчерпаны. В случае, где текущий объект, выбранный из этих двух источников, равны, он произведет тот из первого источника и пропустит их в обоих источниках.

private static IEnumerable<T> Merge<T>(IEnumerable<T> source1,
    IEnumerable<T> source2)
{
    return Merge(source1, source2, Comparer<T>.Default);
}

private static IEnumerable<T> Merge<T>(IEnumerable<T> source1,
    IEnumerable<T> source2, IComparer<T> comparer)
{
    #region Parameter Validation

    if (Object.ReferenceEquals(null, source1))
        throw new ArgumentNullException("source1");
    if (Object.ReferenceEquals(null, source2))
        throw new ArgumentNullException("source2");
    if (Object.ReferenceEquals(null, comparer))
        throw new ArgumentNullException("comparer");

    #endregion

    using (IEnumerator<T>
        enumerator1 = source1.GetEnumerator(),
        enumerator2 = source2.GetEnumerator())
    {
        Boolean more1 = enumerator1.MoveNext();
        Boolean more2 = enumerator2.MoveNext();

        while (more1 && more2)
        {
            Int32 comparisonResult = comparer.Compare(
                enumerator1.Current,
                enumerator2.Current);
            if (comparisonResult < 0)
            {
                // enumerator 1 has the "lowest" item
                yield return enumerator1.Current;
                more1 = enumerator1.MoveNext();
            }
            else if (comparisonResult > 0)
            {
                // enumerator 2 has the "lowest" item
                yield return enumerator2.Current;
                more2 = enumerator2.MoveNext();
            }
            else
            {
                // they're considered equivalent, only yield it once
                yield return enumerator1.Current;
                more1 = enumerator1.MoveNext();
                more2 = enumerator2.MoveNext();
            }
        }

        // Yield rest of values from non-exhausted source
        while (more1)
        {
            yield return enumerator1.Current;
            more1 = enumerator1.MoveNext();
        }
        while (more2)
        {
            yield return enumerator2.Current;
            more2 = enumerator2.MoveNext();
        }
    }
}

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

private static IEnumerable<T> CheapDistinct<T>(IEnumerable<T> source)
{
    return CheapDistinct<T>(source, Comparer<T>.Default);
}

private static IEnumerable<T> CheapDistinct<T>(IEnumerable<T> source,
    IComparer<T> comparer)
{
    #region Parameter Validation

    if (Object.ReferenceEquals(null, source))
        throw new ArgumentNullException("source");
    if (Object.ReferenceEquals(null, comparer))
        throw new ArgumentNullException("comparer");

    #endregion

    using (IEnumerator<T> enumerator = source.GetEnumerator())
    {
        if (enumerator.MoveNext())
        {
            T item = enumerator.Current;

            // scan until different item found, then produce
            // the previous distinct item
            while (enumerator.MoveNext())
            {
                if (comparer.Compare(item, enumerator.Current) != 0)
                {
                    yield return item;
                    item = enumerator.Current;
                }
            }

            // produce last item that is left over from above loop
            yield return item;
        }
    }
}

Примечание, что ни один из них не будет внутренне использовать структуру данных для хранения копии данных, таким образом, они будут дешевыми, если вход будет отсортирован. Если Вы не можете, или не быть, гарантировать, что, необходимо использовать 3,5 дополнительных метода, которые Вы уже нашли.

Вот пример кода, который называет вышеупомянутые методы:

String[] list_1 = { "apple", "orange", "apple", "banana" };
String[] list_2 = { "banana", "pear", "grape" };

Array.Sort(list_1);
Array.Sort(list_2);

IEnumerable<String> items = Merge(
    CheapDistinct(list_1),
    CheapDistinct(list_2));
foreach (String item in items)
    Console.Out.WriteLine(item);
2
ответ дан angry person 27 November 2019 в 02:09
поделиться

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

1
ответ дан petr k. 27 November 2019 в 02:09
поделиться

Вы не знаете, какой подход быстрее, пока Вы не измеряете его. Путь LINQ изящен и легок понять.

Иначе должен реализовать набор как массив хеша (Словарь) и добавить все элементы обоих массивы к набору. Тогда используйте набор. Ключи. ToArray () метод для создания полученного массива.

1
ответ дан danatel 27 November 2019 в 02:09
поделиться
Другие вопросы по тегам:

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