Поиск элемента в списке <T> - PullRequest
       43

Поиск элемента в списке <T>

1 голос
/ 04 октября 2011

У меня есть следующий пример:

public class Commands
{
    public int ID { get; set; }
    public List<string> Alias { get; set; }
}

public class UserAccess
{
    public int AccessID { get; set; }
    // other stuff not needed for the question
    public List<Commands> AllowedCommands { get; set; }
}

Теперь я хотел реализовать в UserAccess способ возврата идентификатора команды или NULL, если в списке не найдено псевдонимов, см. ГрязныйПример того, что я говорю ниже HasCommand:

public class UserAccess
{
    public ID { get; set; }
    // other stuff not needed for the question
    public List<Commands> AllowedCommands { get; set; }

    public Commands HasCommand(string cmd)
    {
        foreach (Commands item in this.AllowedCommands)
        {
            if (item.Alias.Find(x => string.Equals(x, cmd, StringComparison.OrdinalIgnoreCase)) != null)
                return item;
        }
        return null;
    }
}
  • Мой вопрос: каков наиболее эффективный способ запуска или реализации метода HasCommand?

  • Или есть ли лучший способ внедрить его в UserAccess?

Ответы [ 4 ]

6 голосов
/ 04 октября 2011

Можно немного сократить

public Commands HasCommand(string cmd)
{
    return AllowedCommands.FirstOrDefault(c => c.Alias.Contains(cmd, StringComparer.OrdinalIgnoreCase));

}

, но это почти то же самое.

2 голосов
/ 04 октября 2011
public Commands HasCommand(string cmd)
    {
        return this.AllowedCommands.FirstOrDefault(item => item.Alias.Find(x => string.Equals(x, cmd, StringComparison.OrdinalIgnoreCase)) != null);
    }

Вам не нужно использовать Where + FirstOrDefault.FirstOfDefault может иметь условие.

0 голосов
/ 04 октября 2011

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

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

Если ваши псевдонимы могут быть в нижнем регистре, измените их с List на HashSet.Если нет, используйте Словарь, где ключ добавляется в нижнем регистре, а значение - это строка псевдонима (смешанный регистр).Если предположить использование словаря, ваш код будет выглядеть так:

public Commands HasCommand(string cmd)
{
    foreach (Commands item in AllowedCommands)
    {
        if (item.Alias.ContainsKey(cmd))
            return item;
    }
    return null;
}

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

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

Это почти само собой разумеется, но когда вы беспокоитесь о производительности некоторого небольшого куска кода, подобного этому, просто оберните его в цикл for и повторяйте его до тех пор, покавыполнение занимает 5-10 секунд - просто добавьте порядки по мере необходимости, будь то 1000 или 1 000 000 повторений, и добавьте время с помощью System.Diagnostics.Stopwatch.Попробуйте альтернативную логику и повторите тест.5-10 секунд - это минимум, предназначенный для маскировки колебаний, вызванных управляемой средой и другими компонентами, выполняющимися на той же машине (очевидно, следует также избегать запуска других приложений во время теста).Конечно, для общего тестирования производительности сложного приложения рекомендуется использовать инструмент для анализа производительности.

0 голосов
/ 04 октября 2011

Кроме того, 3 предложения для дальнейшего улучшения:

(1) Я бы рекомендовал использовать IEnumerable вместо List, если это возможно.
(2) Я бы назвал «Команды» просто «Командой».
(3) Я бы сделал так, чтобы на все команды можно было легко ссылаться через такой класс:

public class Command {
    public Command(int id, IEnumerable<string> aliases) {
        Id = id;
        Aliases = alias;
    }

    public int Id { get; set; }         
    public IEnumerable<string> Aliases { get; set; }  
}

public class Commands {
    public static readonly Command CommandNameHere1(yourIdHere1, yourAliasesHere1);
    public static readonly Command CommandNameHere2(yourIdHere2, yourAliasesHere2);
    //etc.
}
...