2016-08-17 1 views
2

Я проанализировал сетку данных Excel и создал объектную модель. Есть 32 столбца и 100 000 строк. Меня попросили проверить строки с дублирующимися данными и сообщить о них. Для моей реализации я делаю следующее:C# collection performance: Hashset <string> и словарь <string, IList <int>> самые быстрые коллекции для этой цели?

  1. Использование задач Я строю массив кортежей с идентификатором строки и конкатенированным содержимым ячеек.
  2. I контур через результирующий массив и используя HashSet, я пытаюсь вставить сцепленное значение в HashSet:
  3. Если HashSet.Add() проходит, я создаю новую запись в моем словаре> результата установить для него.
  4. Если HashSet.Add() не могу добавить, что идентификатор строки в существующую запись в мой мой словарь> Результирующий набор

Шаг 1 принимает 0.09s, в то время как остальные принимают 822s к процессу:/Может кто-нибудь где я могу отрубить это время с более подходящим выбором коллекций или алгоритмов?

код ниже:

var results = new Dictionary<string, IList<int>>(numberOfRows); 
var hashSet = new HashSet<string>(); 
var duplicateErrors = new List<string>(); 

for (var row = firstRow; row <= lastRow; row++) 
{ 
    var row1 = row; 
    taskArray[count++] = 
    Task<Tuple<int, string>>.Factory.StartNew(() => GetCompleteRowData(row1, tableRawDataHolders)); 
} 

foreach (var task in taskArray) 
{ 
    if (hashSet.Add(task.Result.Item2)) 
    { 
     results.Add(task.Result.Item2, new List<int>() { task.Result.Item1 }); 
    } 
    else 
    { 
     results[task.Result.Item2].Add(task.Result.Item1); 
    } 
} 

и

public Tuple<int, string> GetCompleteRowData(int row, IEnumerable<ITableRawDataHolder> tableRawDataHolders) 
    { 
     return new Tuple<int, string>(row, string.Join("", 
      tableRawDataHolders.Where(c => c.Row == row).Select(c => c.Value).ToArray())); 
    } 

и

public class TableRawDataHolder : ITableRawDataHolder 
{ 
    public int Row { get; } 
    public int Column { get; } 
    public string Value { get; } 

    public TableRawDataHolder(int row, int column, string value) 
    { 
     Row = row; 
     Column = column; 
     Value = value; 
    } 
} 
+0

Это на самом деле не имеет ничего общего о '' Hashset' против производительности Dictionary'. Название похоже на подобное, но внимательно прочитайте вопрос. OP просит о поиске дубликатов среди 200000 строк. –

+1

* «Шаг 1 занимает 0.09s, а остальное занимает 822s для обработки: /" *. На самом деле это не так. Для асинхронного запуска ваших задач требуется 0,09 с. Однако, когда вы пытаетесь получить доступ к 'task.Result', он блокирует поток. –

+0

Я проголосовал за открытие, так как у @ Йельдара Курмангалиева были веские аргументы. Тем не менее, [этот пост] (http://stackoverflow.com/q/2728500/993547) полезен. –

ответ

2

В этом вопросе ситуация не в словарь или HashSet производительность.

Накладные расходы связаны с тем, как вы читаете данные в GetCompleteRowData и работаете с задачами.

  • Кажется, вы перечисляете полную коллекцию каждый раз, когда вам нужно преобразовать следующую запись.
  • Для каждой следующей записи вы создаете задачу, которая сама добавляет некоторые небольшие накладные расходы. Пока задача не закончится, она просто ждет, когда вы используете task.Result.
  • Также неясно, как быстро ваш ITableRawDataHolder возвращает данные.

Чтобы продемонстрировать чистое значение hashset/dictionary, я создал тест, где я повторяю массив уже подготовленных кортежей. Этот код занимает всего 32 мс на моей машине (i7 quad core).

const Int32 numberOfRows = 200000; 
var inputData = GetInputData(numberOfRows); 
var results = new Dictionary<string, IList<int>>(numberOfRows); 
var hashSet = new HashSet<string>(); 

var sw = Stopwatch.StartNew(); 
foreach (var dataItem in inputData) 
{ 
    if (hashSet.Add(dataItem.Item2)) 
    { 
     results.Add(dataItem.Item2, new List<int>() {dataItem.Item1}); 
    } 
    else 
    { 
     results[dataItem.Item2].Add(dataItem.Item1); 
    } 
} 
Console.WriteLine(sw.ElapsedMilliseconds); 

Вот как я генерации тестовых данных (она включает в себя некоторые фактические дубликаты)

private static List<Tuple<int, String>> GetInputData (int numberOfRows) 
{ 
    var result = new List<Tuple<int, String>>(numberOfRows); 
    var rnd = new Random(); 
    for (var i = 0; i < numberOfRows; i++) 
    { 
     // Once in 100 records we'll have not unique value 
     if (result.Count > 0 && rnd.Next(100)%1 == 0) 
     { 
      result.Add(new Tuple<int, string>(i, result[rnd.Next(result.Count)].Item2)); 
     } 
     else 
      result.Add(new Tuple<int, string>(i, Guid.NewGuid().ToString())); 
    } 
    return result; 
} 
+0

Большое вам спасибо за ваш пример! Это заставило меня задуматься об изменении моего ввода и обернуть его в IDIctionary > с ключом, являющимся номером строки. Мне больше не нужно запрашивать LINQ через все данные для каждой строки, и это сокращает время обработки с 822 секунд до 22 секунд. Очень ценю вашу помощь по этому поводу. –

+0

Я рад, что помог. Хотя 22 секунды все еще выглядят довольно большими. Вы сказали, что получили данные из Excel, возможно, вы читаете этот фрагмент данных из Excel таким образом, который можно оптимизировать. Например, в некоторых случаях может быть быстрее считывать диапазон целых ячеек сразу в массиве, а не читать ячейку по ячейке. – dlxeon

+0

Это целый лист, 32 столбца на 100 000 строк. Я взгляну на код, извлекая его для дальнейших узких мест. –

Смежные вопросы