[英]C# Generic List<T> update items
我正在使用List<T>
並且需要更新列表具有的對象屬性。
什么是最有效/最快的方法? 我知道通過的索引掃描List<T>
作為這個列表的增長會比較慢,而且List<T>
是不是最有效的收集做更新。
感到悲傷,最好是:
存根代碼示例:
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 Aggregation
或SelectMany
,如下所示:
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.