Лучший способ удалить объекты из набора

69
задан Jonathan Wood 27 January 2011 в 23:18
поделиться

9 ответов

Если Вы хотите получить доступ к членам набора одним из их свойств, Вы могли бы рассмотреть использование Dictionary<T> или KeyedCollection<T> вместо этого. Таким образом, Вы не должны искать объект, который Вы ищете.

Иначе, Вы могли, по крайней мере, сделать это:

foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
    if (spAssignment.Member.Name == shortName)
    {
        workspace.RoleAssignments.Remove(spAssignment);
        break;
    }
}
26
ответ дан Liam 24 November 2019 в 13:38
поделиться

Если RoleAssignments List<T>, можно использовать следующий код.

workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);
130
ответ дан Liam 24 November 2019 в 13:38
поделиться

@smaclell спросил, почему обратное повторение было более эффективным в в комментарии к @sambo99.

Иногда это более эффективно. Полагайте, что у Вас есть список людей, и Вы хотите удалить или отфильтровать всех клиентов с кредитным рейтингом < 1000;

у Нас есть следующие данные

"Bob" 999
"Mary" 999
"Ted" 1000

, Если бы мы должны были выполнить итерации вперед, мы скоро попали бы в беду

for( int idx = 0; idx < list.Count ; idx++ )
{
    if( list[idx].Rating < 1000 )
    {
        list.RemoveAt(idx); // whoops!
    }
}

В idx = 0, мы удаляем Bob, который тогда смещает все остающиеся оставленные элементы. Следующий раз через цикл idx = 1, но список [1] теперь Ted вместо Mary. Мы заканчиваем тем, что пропустили Mary по ошибке. Мы могли использовать некоторое время цикл, и мы могли представить больше переменных.

Или, мы просто инвертируем, выполните итерации:

for (int idx = list.Count-1; idx >= 0; idx--)
{
    if (list[idx].Rating < 1000)
    {
        list.RemoveAt(idx);
    }
}

Все индексы налево от удаленного объекта остаются такими же, таким образом, Вы не пропускаете объектов.

тот же принцип применяется при предоставлении списка индексов для удаления из массива. Для хранения вещей прямо, необходимо отсортировать список и затем удалить объекты от самого высокого индекса до самого низкого.

Теперь можно просто использовать Linq и объявить то, что Вы делаете простым способом.

list.RemoveAll(o => o.Rating < 1000);
<час>

Для этого случая удаления единственного объекта, это не более эффективная итерация вперед или назад. Вы могли также использовать Linq для этого.

int removeIndex = list.FindIndex(o => o.Name == "Ted");
if( removeIndex != -1 )
{
    list.RemoveAt(removeIndex);
}
22
ответ дан Robert Paulson 24 November 2019 в 13:38
поделиться

Поскольку простая структура Списка самый эффективный путь, кажется, использует Предикат реализация RemoveAll.

, Например,

 workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);

причины:

  1. метод Predicate/Linq RemoveAll реализован в Списке и имеет доступ к внутреннему массиву, хранящему фактические данные. Это сместит данные и изменит размеры внутреннего массива.
  2. реализация метода RemoveAt является довольно медленной, и скопирует весь основной массив данных в новый массив. Это означает, что обратное повторение бесполезно для Списка

, Если Вы застреваете, реализовывая это в пред эра c# 3.0. У Вас есть 2 опции.

  • легко удобная в сопровождении опция. Скопируйте все объекты соответствия в новый список и и подкачайте базовый список.

, Например,

List<int> list2 = new List<int>() ; 
foreach (int i in GetList())
{
    if (!(i % 2 == 0))
    {
        list2.Add(i);
    }
}
list2 = list2;

Или

  • хитрая немного более быстрая опция, которая вовлекает смещение всех данных в список вниз, когда это не соответствует и затем изменение размеров массива.

при удалении материала действительно часто из списка, возможно, другая структура как HashTable (.net 1.1) или Словарь (.net 2.0) или HashSet (.net 3.5) лучше подходит с этой целью.

10
ответ дан Sam Saffron 24 November 2019 в 13:38
поделиться

Какой тип является набором? Если это - Список, можно использовать полезный "RemoveAll":

int cnt = workspace.RoleAssignments
                      .RemoveAll(spa => spa.Member.Name == shortName)

(Это работает в.NET 2.0. Конечно, если у Вас нет более нового компилятора, необходимо будет использовать "делегата (SPRoleAssignment spa) {спа возврата. Участник. Имя == shortName;}" вместо хорошего синтаксиса лямбды.)

Другой подход, если это не Список, но все еще ICollection:

   var toRemove = workspace.RoleAssignments
                              .FirstOrDefault(spa => spa.Member.Name == shortName)
   if (toRemove != null) workspace.RoleAssignments.Remove(toRemove);

Это требует Счетных дополнительных методов. (Можно скопировать Моно в, если Вы застреваете на.NET 2.0). Если это - некоторый пользовательский набор, который не может взять объект, но ДОЛЖЕН взять индекс, некоторые из других Счетных методов, таких как Выбор, передача в целочисленном индексе для Вас.

7
ответ дан MichaelGG 24 November 2019 в 13:38
поделиться

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

shortname => SPRoleAssignment

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

, К сожалению, если Вы загружаете эти SPRoleAssignments много, который, очевидно, не будет больше экономически эффективным с точки зрения времени. Предложения другие люди, сделанные об использовании Linq, были бы хороши, если Вы используете новую версию Платформы.NET, но иначе, необходимо будет придерживаться метода, который Вы используете.

0
ответ дан Ed Altorfer 24 November 2019 в 13:38
поделиться

Много хороших ответов здесь; мне особенно нравятся лямбда-выражения... очень чистые. Я был небрежен, однако, в не определении типа Набора. Это - SPRoleAssignmentCollection (от MOSS), который только имеет, Удаляют (интервал) и Удаляют (SPPrincipal), не удобный RemoveAll (). Так, я обосновался на этом, если нет лучшее предложение.

foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
                        {
                            if (spAssignment.Member.Name != shortName) continue;
                            workspace.RoleAssignments.Remove((SPPrincipal)spAssignment.Member);
                            break;
                        }
0
ответ дан Dan 24 November 2019 в 13:38
поделиться

Вот довольно хороший способ сделать это

http://support.microsoft.com/kb/555972

        System.Collections.ArrayList arr = new System.Collections.ArrayList();
        arr.Add("1");
        arr.Add("2");
        arr.Add("3");

        /*This throws an exception
        foreach (string s in arr)
        {
            arr.Remove(s);
        }
        */

        //where as this works correctly
        Console.WriteLine(arr.Count);
        foreach (string s in new System.Collections.ArrayList(arr)) 
        {
            arr.Remove(s);
        }
        Console.WriteLine(arr.Count);
        Console.ReadKey();
2
ответ дан 24 November 2019 в 13:38
поделиться

Это мое общее решение

public static IEnumerable<T> Remove<T>(this IEnumerable<T> items, Func<T, bool> match)
    {
        var list = items.ToList();
        for (int idx = 0; idx < list.Count(); idx++)
        {
            if (match(list[idx]))
            {
                list.RemoveAt(idx);
                idx--; // the list is 1 item shorter
            }
        }
        return list.AsEnumerable();
    }

Было бы намного проще, если бы методы расширения поддерживали передачу по ссылке! использование:

var result = string[]{"mike", "john", "ali"}
result = result.Remove(x => x.Username == "mike").ToArray();
Assert.IsTrue(result.Length == 2);

EDIT : гарантирует, что цикл списка остается действительным даже при удалении элементов путем уменьшения индекса (idx).

2
ответ дан 24 November 2019 в 13:38
поделиться
Другие вопросы по тегам:

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