Указатель NULL
- это тот, который указывает на никуда. Когда вы разыскиваете указатель p
, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p
является нулевым указателем, местоположение, хранящееся в p
, является nowhere
, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception
.
В общем, это потому, что что-то не было правильно инициализировано.
string[] result = list1.Union(list2).ToArray();
от msdn: "Этот метод исключает дубликаты из набора возврата. Это - различное поведение к Concat (TSource) метод, который возвращает все элементы во входных последовательностях включая дубликаты".
Почему Вы предполагаете, что это было бы неэффективно? Насколько я знаю, и 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;
}
}
}
. СЕТЕВЫЕ 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,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);
Вероятно, создавая хеш-таблицу с Вашими значениями, поскольку ключи (только уже добавляющий не присутствующих) и затем преобразовывающий ключи к массиву могли быть эффективным решением.
Вы не знаете, какой подход быстрее, пока Вы не измеряете его. Путь LINQ изящен и легок понять.
Иначе должен реализовать набор как массив хеша (Словарь) и добавить все элементы обоих массивы к набору. Тогда используйте набор. Ключи. ToArray () метод для создания полученного массива.