代码之家  ›  专栏  ›  技术社区  ›  CJM

list<object>.removeall-如何创建适当的谓词

  •  39
  • CJM  · 技术社区  · 15 年前

    这是一个有点无意义的问题-我对C和泛型还相当陌生,对谓词、委托和lambda表达式还完全陌生…

    我有一个“查询”类,其中包含另一个称为“车辆”类的通用列表。我正在建立代码,以便从父查询中添加/编辑/删除车辆。现在,我特别关注删除。

    从我目前所读到的内容来看,我似乎可以使用vehicles.removeall()删除具有特定车辆ID的项目或具有特定查询ID的所有项目。我的问题是理解如何进纸。删除所有正确的谓词-我看到的示例过于简单(或者由于我对谓词、委托和lambda表达式缺乏了解,我可能过于简单)。

    所以如果我有 List<Of Vehicle> Vehicles 每辆车都有 EnquiryID ,我将如何使用 Vehicles.RemoveAll() 是否要为给定的查询删除所有车辆?

    我知道有几种方法可以解决这一问题,所以我很想听听这两种方法之间的区别——尽管我需要一些工作,但这也是一个学习练习。

    作为补充问题,通用列表是否是这些对象的最佳存储库?我的第一个爱好是收藏,但似乎已经过时了。当然,仿制药似乎更受欢迎,但我对其他替代品很好奇。

    5 回复  |  直到 7 年前
        1
  •  82
  •   Community CDub    11 年前

    这个 RemoveAll() 方法接受 Predicate<T> 委派(直到这里没有新的内容)。谓词指向只返回true或false的方法。当然, RemoveAll 将从集合中移除所有 T 应用谓词返回true的实例。

    C 3.0允许开发人员使用多种方法将谓词传递给 移除所有 方法(不仅仅是这个方法)。你可以使用:

    lambda表达式

    vehicles.RemoveAll(vehicle => vehicle.EnquiryID == 123);
    

    匿名方法

    vehicles.RemoveAll(delegate(Vehicle v) {
      return v.EnquiryID == 1;
    });
    

    常规方法

    vehicles.RemoveAll(VehicleCustomPredicate);
    private static bool
    VehicleCustomPredicate (Vehicle v) {
        return v.EnquiryID == 1; 
    }
    
        2
  •  15
  •   Quartermeister    15 年前

    t中的谓词是接受t并返回bool的委托。list<t>。remove all将删除调用谓词返回true的列表中的所有元素。提供简单谓词的最简单方法通常是 lambda expression ,但您也可以使用 anonymous methods 或实际方法。

    {
        List<Vehicle> vehicles;
        // Using a lambda
        vehicles.RemoveAll(vehicle => vehicle.EnquiryID == 123);
        // Using an equivalent anonymous method
        vehicles.RemoveAll(delegate(Vehicle vehicle)
        {
            return vehicle.EnquiryID == 123;
        });
        // Using an equivalent actual method
        vehicles.RemoveAll(VehiclePredicate);
    }
    
    private static bool VehiclePredicate(Vehicle vehicle)
    {
        return vehicle.EnquiryID == 123;
    }
    
        3
  •  6
  •   adrianbanks    15 年前

    这应该有效(在哪里 enquiryId 是您需要匹配的ID):

    vehicles.RemoveAll(vehicle => vehicle.EnquiryID == enquiryId);
    

    它的作用是将列表中的每个车辆传递到lambda谓词中,对该谓词进行计算。如果谓词返回真(即 vehicle.EnquiryID == enquiryId ,则当前车辆将从列表中删除。

    如果您知道集合中对象的类型,那么使用通用集合是一种更好的方法。从集合中检索对象时,它避免强制转换,但如果集合中的项是值类型(这可能导致性能问题),它还可以避免装箱。

        4
  •  1
  •   phonetagger    10 年前

    有点偏离主题,但我想从列表中删除所有2。这是一个非常优雅的方法。

    void RemoveAll<T>(T item,List<T> list)
    {
        while(list.Contains(item)) list.Remove(item);
    }
    

    谓语:

    void RemoveAll<T>(Func<T,bool> predicate,List<T> list)
    {
        while(list.Any(predicate)) list.Remove(list.First(predicate));
    }
    

    +1只是为了鼓励你把答案留在这里学习。你也说得对,这不是主题,但我不会因为这个而责备你,因为把你的例子放在这里,再次,严格地用于学习目的,是有重要价值的。我将此回复作为编辑发布,因为将其作为一系列评论发布是不守规矩的。

    虽然您的示例简短紧凑,但在效率方面也不优雅;第一个示例在O(n)方面不好 ,第二个,在O(N)绝对糟糕 )O(n)的算法效率 )是坏的,应该尽可能避免,特别是在通用代码中;O(N)的效率 )很可怕,在任何情况下都应该避免,除非你知道n总是很小。有些人可能会抛出他们的“过早的优化是所有邪恶的”战斗轴的根源,但他们这么做并不明智,因为他们并不真正理解二次增长的后果,因为他们从未编码算法,必须处理大数据集。因此,他们的小数据集处理算法的运行速度一般比他们所能运行的慢,他们不知道他们能运行得更快。高效算法和低效算法之间的差异通常很细微,但性能差异可能很大。了解算法性能的关键是了解选择使用的原语的性能特征。

    在第一个例子中, list.Contains() Remove() 都是O(N),所以A while() 循环,其中一个在谓词中,另一个在主体中是o(n )嗯,从技术上说是O(m*n),但它接近O(n) )当被删除的元素数量(m)接近列表的长度(n)。

    你的第二个例子更糟:o(n 因为每次你打电话 移除() 你也打电话 First(predicate) ,也就是O(n)。想想看: Any(predicate) 循环浏览列表 寻找任何元素 predicate() 返回true。一旦找到第一个这样的元素,它就返回true。在尸体里 当() 循环,然后调用 list.First(predicate) 哪一个 再次循环列表 正在查找已被 list.Any(predicate) . 一次 First() 找到它后,它将返回传递给 list.Remove() 哪一个 第三次循环列表 再次找到之前 Any() 第一() ,以便最终将其移除。一旦移除, 整个过程从一开始就以一个略短的列表重新开始 做所有的循环 一次又一次地从头开始 直到最后,不再保留与谓词匹配的元素。所以第二个例子的性能是O(m*m*n),或者O(n )当m接近n.

    从与某个谓词匹配的列表中删除所有项的最佳方法是使用泛型列表的 List<T>.RemoveAll(predicate) 方法,它是O(n),只要谓词是O(1)。一 for() 循环技术,只传递一次列表,调用 list.RemoveAt() 对于要移除的每个元件,可以 似乎 是O(N),因为它似乎只经过一次循环。这样的解决方案 比你的第一个例子更有效,但是只有一个常数因子,在算法效率方面可以忽略不计。即使是 () 循环实现是O(m*n),因为每次调用 移除() 是O(n)。自从 () 循环本身是O(n),它调用 移除() M次, () 循环的增长是O(n )当m接近n.

        5
  •  1
  •   Wolfzoon    7 年前

    我想谈谈迄今为止所有答案都没有的问题:

    据我目前所读到的,我似乎可以使用 vehicles.removeall()删除具有特定车辆ID的项目。 作为一个补充问题,通用列表是否是这些对象的最佳存储库?

    假设 VehicleID 正如名字所暗示的那样,列表是一种非常低效的存储方式,当你有很多车辆时,就像移除(和其他方法,如 Find )仍然是O(N)。看看 HashSet<Vehicle> 相反,它有O(1)去除(和其他方法),使用:

    int GetHashCode(Vehicle vehicle){return vehicle.VehicleID;}
    int Equals(Vehicle v1, Vehicle v2){return v1.VehicleID == v2.VehicleID;}
    

    删除具有特定查询ID的所有车辆仍然需要以这种方式迭代所有元素,因此可以考虑 GetHashCode 它返回 EnquiryID 相反,这取决于你更经常做什么操作。但是,如果很多车辆都有相同的查询ID,这就有很多碰撞的缺点。

    在这种情况下,更好的选择是 Dictionary<int, List<Vehicle>> 它将查询数据映射到车辆,并在添加/删除车辆时保持最新。然后,将这些车辆从哈希集中移除是一个O(M)操作,其中M是具有特定查询ID的车辆数。