代码之家  ›  专栏  ›  技术社区  ›  Peter Kelly

在列表中的某个索引处查找项目、修改项目然后更新列表的最有效方法是什么?

  •  2
  • Peter Kelly  · 技术社区  · 15 年前

    采取 List<Car> . 各 Car 有一个唯一的索引来识别它,比如 RegNumber 然后另一个描述它的属性-比如 Color 例如。

    我想

    1. 检查收藏品是否有注册号为5的汽车
    2. 如果有,请更改颜色
    3. 如果没有,为那辆车添加一个新项目
    4. 保存列表

    这就是我目前的做法,我在问是否有一种更好、更有效的方法来做到这一点?

    Car car = CarsForSale.Find(c => c.RegNumber == 5);
    
    if (car != null)
    {
       foreach (Car car in CarsForSale)
       {
          if (car.RegNumber == 5)
          {
             car.Color = "Red";
             break;
          }
       }
    }
    else
    {
       CarsForSale.Add(new Car(5, "Red"));
    }
    
    Save(CarsForSale);
    

    编辑 没有多辆车具有相同的注册号-注册号是唯一的,如问题所述。

    不管怎样,这真的只是一个愚蠢的@ss时刻,一个代码评审会发现。谢谢你的回答,也谢谢你没有嘲笑我那明显自命不凡的问题。当然,从集合中返回的项/元素是一个引用,因此绝对不需要再次遍历列表…是时候让我的头撞到墙上了。

    5 回复  |  直到 15 年前
        1
  •  4
  •   ChrisF    15 年前

    好吧,首先你不需要你的测试 car.RegNumber == 5 在循环中-你已经从你的陈述中找到了第一辆符合这个标准的车:

    Car car = CarsForSale.Find(c => c.RegNumber == 5);
    

    实际上,您的整个循环是多余的,您可以只拥有:

    if (car != null)
    {
        car.Color = "Red";
    }
    else
    {
        CarsForSale.Add(new Car(5, "Red"));
    }
    

    除非你想找到所有 RegNumber 等于5,在这种情况下,您的第一行是不正确的,因为这只会找到符合标准的第一辆车。为了找到所有你想要的车,沿着这些路线:

    var cars = CarsForSale.Where(c => c.RegNumber == 5);
    
    foreach (Car car in cars)
    {
        car.Color = "Red";
    }
    
    if (!car.Any())
    {
        CarsForSale.Add(new Car(5, "Red"));
    }
    

    对于原始代码,编译器应该警告您重新定义 car 在循环中会隐藏原始定义(我引用的定义)。

        2
  •  2
  •   djdd87    15 年前

    当您已经有了一个结果时,为什么要对列表进行迭代?

    这将达到相同的结果:

    Car car = CarsForSale.Find(c => c.RegNumber == 5);
    if (car != null)
    {
       car.Color = "Red";
    }
    else
    {
       CarsForSale.Add(new Car(5, "Red"));
    }
    Save(CarsForSale);
    

    结果来自 Find 方法 CarsForSale 如果返回结果,则将是引用类型,这意味着对 car 将在中更改项目 汽车销售 也。我猜你认为 发现 将与中的实际项断开连接 汽车销售 因此不必要的foreach循环?

        3
  •  2
  •   Dan Tao    15 年前

    更新

    对于这一评论,您留下了一些其他答案:

    如果有几辆车 注册号是5?

    如果有可能多辆车有相同的 RegNumber 然后打电话 Find 不是正确的方法。 发现 只是在列表中枚举以查找匹配项;最好不要跳过它并保留 foreach 循环。

    但是,您可以使用 Where 而是:

    var matches = CarsForSale.Where(c => c.RegNumber == 5);
    int numMatches = 0;
    
    foreach (Car match in matches )
    {
        match.Color = "Red";
        ++numMatches;
    }
    if (numMatches == 0)
    {
       CarsForSale.Add(new Car(5, "Red"));
    }
    

    原始答案

    整个 前额 循环是多余的:您基本上是通过调用 发现 .

    因此,代码可以简化:

    Car car = CarsForSale.Find(c => c.RegNumber == 5);
    
    if (car != null)
    {
        car.Color = "Red";
    }
    else
    {
       CarsForSale.Add(new Car(5, "Red"));
    }
    

    也就是说,如果你在车里 List<Car> 通过 重新编号 ,使用 Dictionary<int, Car> 而不是 清单&汽车; :

    Car car;
    if (CarsForSale.TryGetValue(5, out car))
    {
        car.Color = "Red";
    }
    else
    {
        CarsForSale[5] = car = new Car(5, "Red");
    }
    
        4
  •  2
  •   Justin Niessner    15 年前

    一旦您拥有了Linq语句中要查找的汽车,就无需循环查看集合以查找匹配项:

    Car car = CarsForSale.Where(c => c.RegNumber == 5).FirstOrDefault();
    
    if(car != null)
    {
        car.Color = "Red";
    }
    else
    {
        CarsForSale.Add(new Car(5, "Red"));
    }
    
    Save(CarsForSale);
    

    或者如果有多辆车具有相同的注册号:

    var cars = CarsForSale.Where(c => c.RegNumber == 5);
    
    if(cars.Any())
    {
        foreach(Car car in cars)
            car.Color = "Red";
    }
    else
    {
        CarsForSale.Add(new Car(5, "Red"));
    }
    
    Save(CarsForSale);
    
        5
  •  1
  •   Oliver    15 年前

    正如丹已经提到的,如果你有一个独特的属性,你应该使用它作为 Dictionary<TKey, TValue> .

    因为检查字典中是否有东西是O(1)操作,而在列表中,在最坏的情况下只是O(n)(现在假设您的列表中有100万辆车)。

    var carsForSale = new Dictionary<int, Car>();
    
    //Create a car which you like to check
    var checkCar = new Car(4, Color.Red);
    
    //Use this approach if you want to change only a few properties
    //of an existing item
    if (carsForSale.ContainsKey(checkCar.RegNum))
    {
        carsForSale[checkCar.RegNum].Color = checkCar.Color;
    }
    else
    {
        carsForSale[4] = checkCar;
    }
    
    //If you have to take over ALL property settings, you can also
    //forget the old item and take the new one.
    //The index operator is smart enough to just add a new one
    //or to delete an old and add the new in one step.
    carsForSale[checkCar.RegNum] = checkCar;
    

    car类的虚拟实现:

    public class Car
    {
        public int RegNum { get; private set; }
        public Color Color { get; set; }
    
        public Car(int regNum)
            : this(regNum, Color.Empty)
        { }
    
        public Car(int regNum, Color color)
        {
            RegNum = regNum;
            Color = color;
        }
    }
    

    这个 问题 使用字典的原因是,因为您想明确地告诉密钥是什么(汽车的regnum属性),但也可以使用 Hashset<T> 如果你的汽车对象能正确实现 Equals() GetHashCode() 但这比你想象的要复杂一点。一个很好的解释可以在 Essentials C# 书。