Herkes C # genel bir listeyi çoğaltmak için hızlı bir yöntem var mı?
ICollection<MyClass> withoutDuplicates = new HashSet<MyClass>(inputList);
Herkes C # genel bir listeyi çoğaltmak için hızlı bir yöntem var mı?
ICollection<MyClass> withoutDuplicates = new HashSet<MyClass>(inputList);
Yanıtlar:
Belki de bir HashSet kullanmayı düşünmelisiniz .
MSDN bağlantısından:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
HashSet<int> evenNumbers = new HashSet<int>();
HashSet<int> oddNumbers = new HashSet<int>();
for (int i = 0; i < 5; i++)
{
// Populate numbers with just even numbers.
evenNumbers.Add(i * 2);
// Populate oddNumbers with just odd numbers.
oddNumbers.Add((i * 2) + 1);
}
Console.Write("evenNumbers contains {0} elements: ", evenNumbers.Count);
DisplaySet(evenNumbers);
Console.Write("oddNumbers contains {0} elements: ", oddNumbers.Count);
DisplaySet(oddNumbers);
// Create a new HashSet populated with even numbers.
HashSet<int> numbers = new HashSet<int>(evenNumbers);
Console.WriteLine("numbers UnionWith oddNumbers...");
numbers.UnionWith(oddNumbers);
Console.Write("numbers contains {0} elements: ", numbers.Count);
DisplaySet(numbers);
}
private static void DisplaySet(HashSet<int> set)
{
Console.Write("{");
foreach (int i in set)
{
Console.Write(" {0}", i);
}
Console.WriteLine(" }");
}
}
/* This example produces output similar to the following:
* evenNumbers contains 5 elements: { 0 2 4 6 8 }
* oddNumbers contains 5 elements: { 1 3 5 7 9 }
* numbers UnionWith oddNumbers...
* numbers contains 10 elements: { 0 2 4 6 8 1 3 5 7 9 }
*/
HashSet
bir indeksi yoktur , bu nedenle kullanmak her zaman mümkün değildir. Bir kez yinelenmeden büyük bir liste oluşturmak ve sonra ListView
sanal modda kullanmak zorunda . Bir HashSet<>
ilk yapmak ve daha sonra bir haline dönüştürmek süper hızlıydı List<>
(böylece ListView
öğelere indeksle erişebilir). List<>.Contains()
çok yavaş.
Net 3+ kullanıyorsanız, Linq'i kullanabilirsiniz.
List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();
Nasıl olur:
var noDupes = list.Distinct().ToList();
Net 3.5 içinde mi?
Aynı türden bir Liste ile bir HashSet başlatmanız yeterlidir:
var noDupes = new HashSet<T>(withDupes);
Veya bir Listenin döndürülmesini istiyorsanız:
var noDupsList = new HashSet<T>(withDupes).ToList();
List<T>
sonuç olarak kullanmanız gerekiyorsanew HashSet<T>(withDupes).ToList()
Sıralayın, daha sonra birbirlerinin yanındaki iki ve ikiyi kontrol edin, çünkü kopyalar birbirine yapışacaktır.
Bunun gibi bir şey:
list.Sort();
Int32 index = list.Count - 1;
while (index > 0)
{
if (list[index] == list[index - 1])
{
if (index < list.Count - 1)
(list[index], list[list.Count - 1]) = (list[list.Count - 1], list[index]);
list.RemoveAt(list.Count - 1);
index--;
}
else
index--;
}
Notlar:
RemoveAt
bir çok pahalı bir operasyonList
Bu komutu kullanmayı seviyorum:
List<Store> myStoreList = Service.GetStoreListbyProvince(provinceId)
.GroupBy(s => s.City)
.Select(grp => grp.FirstOrDefault())
.OrderBy(s => s.City)
.ToList();
Listemde şu alanlar var: Id, StoreName, City, PostalCode Yinelenen değerleri olan bir açılır menüde şehirlerin listesini göstermek istedim. çözüm: Şehre göre gruplandırın ve ardından liste için ilkini seçin.
Umut ediyorum bu yardım eder :)
Benim için çalıştı. sadece kullan
List<Type> liIDs = liIDs.Distinct().ToList<Type>();
"Tür" ü istediğiniz türle değiştirin, örn. İnt.
Kronoz'un .Net 3.5'te dediği gibi kullanabilirsiniz Distinct()
.
Net 2'de bunu taklit edebilirsiniz:
public IEnumerable<T> DedupCollection<T> (IEnumerable<T> input)
{
var passedValues = new HashSet<T>();
// Relatively simple dupe check alg used as example
foreach(T item in input)
if(passedValues.Add(item)) // True if item is new
yield return item;
}
Bu, herhangi bir koleksiyonu çıkarmak için kullanılabilir ve değerleri orijinal sırayla döndürür.
Normalde bir koleksiyonu filtrelemek (hem Distinct()
bu örnekte hem de bu örnekte olduğu gibi) öğeleri kaldırmaktan çok daha hızlıdır .
HashSet
yapıcısının tekilleştirildiğini fark etmedim, bu da çoğu koşul için daha iyi hale getiriyor. Ancak, bu sıralama düzenini koruyacaktır, ki bu da bir HashSet
.
Dictionary<T, object>
yerine, yerini .Contains
ile .ContainsKey
ve .Add(item)
ile.Add(item, null)
HashSet
siparişi korurken Distinct()
değil koruyor .
Bir uzatma yöntemi böyle bir şeye gitmenin iyi bir yolu olabilir:
public static List<T> Deduplicate<T>(this List<T> listToDeduplicate)
{
return listToDeduplicate.Distinct().ToList();
}
Ve sonra böyle arayın, örneğin:
List<int> myFilteredList = unfilteredList.Deduplicate();
Java (C # aşağı yukarı aynı olduğunu varsayalım):
list = new ArrayList<T>(new HashSet<T>(list))
Orijinal listeyi gerçekten değiştirmek istiyorsanız:
List<T> noDupes = new ArrayList<T>(new HashSet<T>(list));
list.clear();
list.addAll(noDupes);
Siparişi korumak için HashSet'i LinkedHashSet ile değiştirin.
var noDupes = new HashSet<T>(list); list.Clear(); list.AddRange(noDupes);
:)
Bu farklı (öğeleri kopyalamayan öğeler) alır ve tekrar listeye dönüştürür:
List<type> myNoneDuplicateValue = listValueWithDuplicate.Distinct().ToList();
Linq's Union yöntemini kullanın .
Not: Bu çözüm, Linq hakkında bir bilgi gerektirmez, bunun dışında.
kod
Sınıf dosyanızın üstüne aşağıdakileri ekleyerek başlayın:
using System.Linq;
Şimdi, aşağıdaki nesnelerden yinelenenleri kaldırmak için aşağıdakileri kullanabilirsiniz obj1
:
obj1 = obj1.Union(obj1).ToList();
Not: obj1
Nesnenin adıyla yeniden adlandırın .
Nasıl çalışır
Birlik komutu iki kaynak nesnenin her girişinden birini listeler. Obj1 her iki kaynak nesne olduğundan, obj1 her girişten birine indirgenir.
ToList()
Yeni bir List döndürür. Linq komutları Union
, sonucu orijinal Listeyi değiştirmek veya yeni bir Liste döndürmek yerine IEnumerable sonucu olarak döndürdüğünden bu gereklidir .
Yardımcı bir yöntem olarak (Linq olmadan):
public static List<T> Distinct<T>(this List<T> list)
{
return (new HashSet<T>(list)).ToList();
}
Siparişin umurumda değil Eğer sadece içine öğeleri dürtme HashSet
eğer, do böyle bir şey yapabileceği düzeni korumak istiyorum:
var unique = new List<T>();
var hs = new HashSet<T>();
foreach (T t in list)
if (hs.Add(t))
unique.Add(t);
Veya Linq yolu:
var hs = new HashSet<T>();
list.All( x => hs.Add(x) );
Düzenleme:HashSet
yöntemdir O(N)
zaman ve O(N)
sıralama yaparken uzay ve (@ tarafından önerildiği gibi daha sonra benzersiz hale lassevk ve diğerleri) 'dir O(N*lgN)
zaman ve O(1)
sıralama yolu aşağı olduğu (ilk bakışta olduğu gibi) o kadar bana açık değil bu yüzden uzay (benim geçici oylama için özür dileriz ...)
İşte bitişik kopyaları yerinde kaldırmak için bir genişletme yöntemi. Önce Sort () öğesini çağırın ve aynı IComparer'a geçin. Bu Lasse V. Karlsen'in RemoveAt'ı tekrar tekrar çağıran versiyonundan daha verimli olmalıdır (çoklu blok bellek hareketlerine neden olur).
public static void RemoveAdjacentDuplicates<T>(this List<T> List, IComparer<T> Comparer)
{
int NumUnique = 0;
for (int i = 0; i < List.Count; i++)
if ((i == 0) || (Comparer.Compare(List[NumUnique - 1], List[i]) != 0))
List[NumUnique++] = List[i];
List.RemoveRange(NumUnique, List.Count - NumUnique);
}
Yinelenenlerin listeye eklenmediğinden emin olmak daha kolay olabilir.
if(items.IndexOf(new_item) < 0)
items.add(new_item)
List<T>.Contains
Her seferinde yöntemi kullanıyorum ancak 1.000.000'dan fazla girişle. Bu işlem başvurumu yavaşlatır. Onun List<T>.Distinct().ToList<T>()
yerine bir ilk kullanıyorum.
Net 2.0'da başka bir yol
static void Main(string[] args)
{
List<string> alpha = new List<string>();
for(char a = 'a'; a <= 'd'; a++)
{
alpha.Add(a.ToString());
alpha.Add(a.ToString());
}
Console.WriteLine("Data :");
alpha.ForEach(delegate(string t) { Console.WriteLine(t); });
alpha.ForEach(delegate (string v)
{
if (alpha.FindAll(delegate(string t) { return t == v; }).Count > 1)
alpha.Remove(v);
});
Console.WriteLine("Unique Result :");
alpha.ForEach(delegate(string t) { Console.WriteLine(t);});
Console.ReadKey();
}
Çözmenin birçok yolu vardır - Listede yinelenen sorun, aşağıda bunlardan biri:
List<Container> containerList = LoadContainer();//Assume it has duplicates
List<Container> filteredList = new List<Container>();
foreach (var container in containerList)
{
Container duplicateContainer = containerList.Find(delegate(Container checkContainer)
{ return (checkContainer.UniqueId == container.UniqueId); });
//Assume 'UniqueId' is the property of the Container class on which u r making a search
if(!containerList.Contains(duplicateContainer) //Add object when not found in the new class object
{
filteredList.Add(container);
}
}
Şerefe Ravi Ganesan
İşte, okunması zor LINQ veya listenin önceden sıralanmasını gerektirmeyen basit bir çözüm.
private static void CheckForDuplicateItems(List<string> items)
{
if (items == null ||
items.Count == 0)
return;
for (int outerIndex = 0; outerIndex < items.Count; outerIndex++)
{
for (int innerIndex = 0; innerIndex < items.Count; innerIndex++)
{
if (innerIndex == outerIndex) continue;
if (items[outerIndex].Equals(items[innerIndex]))
{
// Duplicate Found
}
}
}
}
David J.'nin cevabı iyi bir yöntemdir, ekstra nesnelere gerek yoktur, sıralama vb.
for (int innerIndex = items.Count - 1; innerIndex > outerIndex ; innerIndex--)
Böylece dış döngü tüm liste için en altta, ancak iç döngü "dış döngü konumuna ulaşılana kadar" aşağı iner.
Dış döngü, tüm listenin işlendiğinden emin olur, iç döngü gerçek kopyaları bulur, bunlar yalnızca dış döngünün henüz işlenmediği kısımda olabilir.
Veya iç döngü için aşağıdan yukarıya yapmak istemiyorsanız, iç döngü dışIndex + 1'den başlatabilirsiniz.
Tüm yanıtlar listeleri kopyalar ya da yeni bir liste oluşturur ya da yavaş işlevler kullanır ya da çok yavaştır.
Anladığım kadarıyla, bu bildiğim en hızlı ve en ucuz yöntem (ayrıca, gerçek zamanlı fizik optimizasyonu konusunda uzmanlaşmış çok deneyimli bir programcı tarafından destekleniyor).
// Duplicates will be noticed after a sort O(nLogn)
list.Sort();
// Store the current and last items. Current item declaration is not really needed, and probably optimized by the compiler, but in case it's not...
int lastItem = -1;
int currItem = -1;
int size = list.Count;
// Store the index pointing to the last item we want to keep in the list
int last = size - 1;
// Travel the items from last to first O(n)
for (int i = last; i >= 0; --i)
{
currItem = list[i];
// If this item was the same as the previous one, we don't want it
if (currItem == lastItem)
{
// Overwrite last in current place. It is a swap but we don't need the last
list[i] = list[last];
// Reduce the last index, we don't want that one anymore
last--;
}
// A new item, we store it and continue
else
lastItem = currItem;
}
// We now have an unsorted list with the duplicates at the end.
// Remove the last items just once
list.RemoveRange(last + 1, size - last - 1);
// Sort again O(n logn)
list.Sort();
Son maliyet:
nlogn + n + nlogn = n + 2nlogn = O (nlogn) ki bu oldukça hoş.
RemoveRange hakkında not: Listenin sayısını ayarlayamadığımız ve İşlevleri kaldır'ı kullanamadığımız için, bu işlemin hızını tam olarak bilmiyorum ama sanırım en hızlı yol bu.
Yedekte sınıflarınız varsa Product
ve Customer
yinelenen öğeleri listelerinden kaldırmak istiyorsak
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string CustomerName { get; set; }
}
Aşağıdaki formda bir genel sınıf tanımlamanız gerekir
public class ItemEqualityComparer<T> : IEqualityComparer<T> where T : class
{
private readonly PropertyInfo _propertyInfo;
public ItemEqualityComparer(string keyItem)
{
_propertyInfo = typeof(T).GetProperty(keyItem, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
}
public bool Equals(T x, T y)
{
var xValue = _propertyInfo?.GetValue(x, null);
var yValue = _propertyInfo?.GetValue(y, null);
return xValue != null && yValue != null && xValue.Equals(yValue);
}
public int GetHashCode(T obj)
{
var propertyValue = _propertyInfo.GetValue(obj, null);
return propertyValue == null ? 0 : propertyValue.GetHashCode();
}
}
ardından listenizdeki yinelenen öğeleri kaldırabilirsiniz.
var products = new List<Product>
{
new Product{ProductName = "product 1" ,Id = 1,},
new Product{ProductName = "product 2" ,Id = 2,},
new Product{ProductName = "product 2" ,Id = 4,},
new Product{ProductName = "product 2" ,Id = 4,},
};
var productList = products.Distinct(new ItemEqualityComparer<Product>(nameof(Product.Id))).ToList();
var customers = new List<Customer>
{
new Customer{CustomerName = "Customer 1" ,Id = 5,},
new Customer{CustomerName = "Customer 2" ,Id = 5,},
new Customer{CustomerName = "Customer 2" ,Id = 5,},
new Customer{CustomerName = "Customer 2" ,Id = 5,},
};
var customerList = customers.Distinct(new ItemEqualityComparer<Customer>(nameof(Customer.Id))).ToList();
bu kod, yinelenen öğeleri Id
başka bir özelliğe göre kaldırmak isterseniz, nameof(YourClass.DuplicateProperty)
aynı nameof(Customer.CustomerName)
öğeleri değiştirebilir ve ardından yinelenen öğeleri CustomerName
Mülke göre kaldırabilirsiniz .
public static void RemoveDuplicates<T>(IList<T> list )
{
if (list == null)
{
return;
}
int i = 1;
while(i<list.Count)
{
int j = 0;
bool remove = false;
while (j < i && !remove)
{
if (list[i].Equals(list[j]))
{
remove = true;
}
j++;
}
if (remove)
{
list.RemoveAt(i);
}
else
{
i++;
}
}
}
Basit, sezgisel bir uygulama:
public static List<PointF> RemoveDuplicates(List<PointF> listPoints)
{
List<PointF> result = new List<PointF>();
for (int i = 0; i < listPoints.Count; i++)
{
if (!result.Contains(listPoints[i]))
result.Add(listPoints[i]);
}
return result;
}