簡體   English   中英

C#通用列表 <T> 更新項目

[英]C# Generic List<T> update items

我正在使用List<T>並且需要更新列表具有的對象屬性。

什么是最有效/最快的方法? 我知道通過的索引掃描List<T>作為這個列表的增長會比較慢,而且List<T>是不是最有效的收集做更新。

感到悲傷,最好是:

  • 刪除匹配對象,然后添加一個新對象?
  • 瀏覽列表索引,直到找到匹配的對象,然后更新對象的屬性?
  • 如果我有一個集合,讓我們使用IEnumerable,並且我想將該IEnumerable更新到列表中,那是最好的方法。

存根代碼示例:

public class Product
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public string Category { get; set; }
}

public class ProductRepository
{
    List<Product> product = Product.GetProduct();
    public void UpdateProducts(IEnumerable<Product> updatedProduct)
    {
    }
    public void UpdateProduct(Product updatedProduct)
    {
    }
}

如果要快速查找,可以考慮使用Dictionary而不是List。 在您的情況下,它將是乘積ID(我認為是唯一的)。 字典MSDN

例如:

public class ProductRepository
    {
        Dictionary<int, Product> products = Product.GetProduct();
        public void UpdateProducts(IEnumerable<Product> updatedProducts)
        {
            foreach(var productToUpdate in updatedProducts)
            {
                UpdateProduct(productToUpdate);
            }

            ///update code here...
        }
        public void UpdateProduct(Product productToUpdate)
        {
            // get the product with ID 1234 
            if(products.ContainsKey(productToUpdate.ProductId))
            {
                var product = products[productToUpdate.ProductId];
                ///update code here...
                product.ProductName = productToUpdate.ProductName;
            }
            else
            {
                //add code or throw exception if you want here.
                products.Add(productToUpdate.ProductId, productToUpdate);
            }
        }
    }

效率到底是什么?

除非從字面上看有成千上萬的項目在進行foreach,或者for或任何其他類型的循環操作最有可能僅顯示毫秒數。 真? 因此,您在尋找最佳性能上浪費了更多的時間(程序員的成本為每小時XX美元,而不是最終用戶的成本)。

因此,如果您實際上有成千上萬的記錄,我建議通過使用Parallel.Foreach方法並行處理列表來提高效率,該方法可以處理更多記錄以節省線程開銷。


恕我直言,如果記錄數大於100,則表示存在數據庫正在使用。 如果涉及數據庫,則編寫一個更新程序並一天調用一次; 我將很難編寫一個一次性程序來進行特定的更新 ,而該更新可以在所述數據庫中以更簡單的方式完成。

您的用例正在更新List<T> ,它可以包含數百萬條記錄,並且更新后的記錄可以是子列表,也可以只是一條記錄

以下是架構:

public class Product
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public string Category { get; set; }
}

Product是否包含主鍵,這意味着可以唯一地標識每個Product對象,並且沒有重復項,並且每個更新都針對單個唯一記錄?

如果為Yes ,則最好以Dictionary<int,T>的形式排列List<T> ,這對於IEnumerable<T>意味着每次更新將是O(1)時間復雜度,並且這意味着所有可以根據IEnumerable<T>的大小來完成更新,我預計它不會很大,盡管會為不同的數據結構分配額外的內存,但這將是一個非常快速的解決方案。@ JamieLupton已經提供了類似的解決方案

如果重復Product ,則沒有主鍵,則上述解決方案無效,然后搜索List<T>理想方法是二進制搜索,其時間復雜度為O(logN)

現在,由於IEnumerable<T>大小相對較小(例如M),因此總體時間復雜度將為O(M*logN) ,其中M遠小於N且可以忽略。

List<T>支持Binary Search API,它提供元素索引,然后可用於更新相關索引處的對象,請在此處查看示例

對於我來說,對於如此大量的記錄,最佳選擇將是並行處理以及二進制搜索

現在,由於線程安全是個問題,所以我通常要做的是將List<T>划分為List<T>[] ,因為這樣每個單元都可以分配給一個單獨的線程,一種簡單的方法是使用MoreLinq批處理Api,其中您可以使用Environment.ProcessorCount來獲取系統處理器的數量,然后按如下所示創建IEnumerable<IEnumerable<T>>

var enumerableList = List<T>.Batch(Environment.ProcessorCount).ToList();

另一種方法是遵循自定義代碼:

public static class MyExtensions
{
    // data - List<T>
    // dataCount - Calculate once and pass to avoid accessing the property everytime
    // Size of Partition, which can be function of number of processors
    public static List<T>[] SplitList<T>(this List<T> data, int dataCount, int partitionSize)
    {
        int remainderData;    
        var fullPartition = Math.DivRem(dataCount, partitionSize, out remainderData);    
        var listArray = new List<T>[fullPartition];    
        var beginIndex = 0;

        for (var partitionCounter = 0; partitionCounter < fullPartition; partitionCounter++)
        {
            if (partitionCounter == fullPartition - 1)
                listArray[partitionCounter] = data.GetRange(beginIndex, partitionSize + remainderData);
            else
                listArray[partitionCounter] = data.GetRange(beginIndex, partitionSize);    
            beginIndex += partitionSize;
        }    
        return listArray;
    }
}

現在,您可以創建Task[] ,在上面生成的List<T>[]上為每個元素List<T>分配每個Task ,然后對每個子分區進行二進制搜索。 盡管它具有重復性,但將使用並行處理和二進制搜索的功能。 可以啟動每個Task ,然后我們可以使用Task.WaitAll(taskArray)等待任務處理完成

除此之外,如果您要創建Dictionary<int,T>[]並因此使用並行處理,那么這將是最快的。

List<T>[]List<T>最終集成可以使用Linq AggregationSelectMany ,如下所示:

List<T>[] splitListArray = Fetch splitListArray;

// Process  splitListArray

var finalList = splitListArray.SelectMany(obj => obj).ToList()

另一個選擇是將Parallel.ForEach與線程安全數據結構(例如ConcurrentBag<T>或者如果要替換完整對象,則可以是ConcurrentDictionary<int,T> ,但是如果其屬性更新,則使用簡單的List<T>會工作。 Parallel.ForEach內部使用范圍分區器,類似於我上面建議的范圍

理想情況下,上述解決方案取決於您的用例,您將能夠組合使用以獲得最佳結果。 讓我知道,如果您需要特定的示例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM