This question already has answers here:
Array versus List<T>: When to use which?
(16 answers)
Closed 7 years ago.
In the below code,
is it more efficient (in terms of memory and time) to create a List<string> directly, rather than creating a string[] and calling ToList() on it before passing it to SomeMethod()?
If you create the string[] and call ToList(), would it create a List<string> object, as well as already having the string[]?
The reason I ask is that I have seen code where they are creating a string[] and calling ToList() before passing it as an argument to a method with a List<string> parameter, and I wasn't sure if there was any particular reason for that.
class Program
{
static void Main(string[] args)
{
var array = new string[] { "str1", "str2" };
SomeMethod(array.ToList());
var list = new List<string> { "str1", "str2" };
SomeMethod(list);
}
static void SomeMethod(List<string> list)
{
//do stuff
}
}
Every .ToList() and .ToArray() creates new objects, allocates memory, copies values. So, try to minimize such operations
Use more common types: IEnumerable, ICollection. Because both list and array are suitable for IEnumerable, for example:
class Program
{
static void Main(string[] args)
{
var array = new string[] { "str1", "str2" };
SomeMethod(array);
var list = new List<string> { "str1", "str2" };
SomeMethod(list);
}
static void SomeMethod(IEnumerable<string> list)
{
//do stuff
}
}
Reference.
ToArray Method of List:
public T[] ToArray()
{
T[] objArray = new T[this._size];
Array.Copy((Array) this._items, 0, (Array) objArray, 0, this._size);
return objArray;
}
You can see, it copies data.
ToList method:
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
throw Error.ArgumentNull("source");
return new List<TSource>(source);
}
You can see, it creates new list object. And constructor List(IEnumerable<T> collection) copies data.
I'd expect it to be more memory efficient to go straight to the List, but time would depend on inner workings. ToList would probably consist of creating a list and then moving the data over from the array, however, which seems inefficient in terms of time too.
Try running some tests doing each method 100000 or so times, and seeing the amount of time this takes in each test.
Related
I wrote the folowing method :
// Merge two ArrayLists to the first one without duplication
public static void mergeIntoFirst(ArrayList array1, ArrayList array2)
{
if(array1==null | array2==null)
throw new ArgumentNullException();
if (array1 == array2)
return; //if they are pointing to the same array, then we can exit.
foreach (object obj in array2)
{
if (!array1.Contains(obj))
array1.Add(obj);
}
}
But now I want to change the my program to work with linkedList insted,
because arraylist doesn't work well with linq as far as I know...
But I need the input to be generc, and work with all linked list kind, just like here the ArrayList can contains all sort of objects.
(I'm using this method twice in my code, once with array of users, and the other with array of messages sent by the users)
I thought that using LinkedList<object> will solve it, since anything is object (exept int, chat double itc)
but it throws a casting exeption when running...
what shoul'd I do then?
Thanks!
Here's an implementation of your code that should work for any ICollection<T> of which both LinkedList<T> and List<T> implement. Note that you have to define the generic type on the method.
public static void MergeIntoFirst<T>(ICollection<T> c1, ICollection<T> c2)
{
if(c1==null || c2==null)
throw new ArgumentNullException();
if (c1 == c2)
return; //if they are pointing to the same array, then we can exit.
foreach (T item in c2)
{
if (!c1.Contains(item))
c1.Add(item);
}
}
Then you could use it like this.
List<int> l1 = new List<int> { 1, 2, 3 };
List<int> l2 = new List<int> { 2, 3, 4 };
MergeIntoFirst(l1, l2);
Console.WriteLine(string.Join(",", l1));
// outputs: 1, 2, 3, 4
Note that this is close to what Enumerable.Union does except that it would remove duplicates that exist in both collections and produces a new collection rather than mutating the first one.
Is there a simpler way to write the following? I.E., without the lambda.
var strings = new[] { "Alabama", "Mississippi", "Louisiana" };
var ordered = strings.OrderBy(x => x);
Seems like it should be possible, since string implements IEquatable<string>.
It's IComparable that matters more thanIEquatable here, but it is possible:
Array.Sort(strings);
This works because strings is already an array. Since you asked for any IEnumerable:
var ary = strings.ToArray();
Array.Sort(ary);
Note the extra variable is also important in this second sample, because Array.Sort() sorts the actual object passed without returning the results, and calling .ToArray() created a new array that was then thrown away. Without the extra variable, you lose your work.
There is a similar sort method on the List<T> object you can use, as well.
You can also make your own extension method for this:
public static class MyExtensions
{
public static IOrderedEnumerable<T> Sort(this IEnumerable<T> items) where T : IComparable
{
return items.OrderBy(i => i);
}
}
And now you could just say:
var ordered = strings.Sort();
For .NET 7 or higher, use Order.
var strings = new[] { "Alabama", "Mississippi", "Louisiana" };
var ordered = strings.Order();
dotnet/runtime#67194
In Visual Studio, ReSharper warns: "Possible multiple enumeration of IEnumerable" for the following code:
static void Main(string[] args)
{
IEnumerable<string> items = Test2();
foreach (var item in items)
{
Console.WriteLine(item);
}
var newitems = new StringBuilder();
foreach (var item in items)
{
newitems.Append(item);
}
}
private static IEnumerable<string> Test2()
{
string[] array1 = { "1", "2", "3" };
return array1;
}
I expect that the Test2 method will be called twice, but it's called once.
What am I missing?
It's only called once because Test2() actually returns string [] which is also an IEnumerable<string>.
This string [] array remains referenced by items so each time you use items you just re-use the array.
The case you're expecting is an implementation of Test2() with an iterator block :
private static IEnumerable<string> Test2()
{
string[] array1 = { "1", "2", "3" };
foreach (var str in array1)
{
yield return str;
}
}
Take a look at this example:
void Main()
{
IEnumerable<int> items = Test2();
foreach (var item in items)
{
Console.WriteLine(item);
}
var newitems = new StringBuilder();
foreach (var item in items)
{
newitems.Append(item);
}
}
IEnumerable<int> Test2()
{
Console.WriteLine("Test2 called");
return GetEnum();
}
IEnumerable<int> GetEnum()
{
for(var i = 0; i < 5; i ++)
{
Console.WriteLine("Doing work...");
Thread.Sleep(50); //Download some information from a website, or from a database
yield return i;
}
}
Imagine that return GetEnum(); was return new int[] { 1, 2, 3 }
Now, with arrays, iterating them multiple times isn't necessarily a bad thing. In your case, you can do the work in one loop, but that's not the reason resharper warns you. It warns you because of the possibility that Test2() returns a lazy enumerable that does work every time it's iterated.
If you run the above code, you'll get this output:
Test2 called
Doing work...
0
Doing work...
1
Doing work...
2
Doing work...
3
Doing work...
4
Doing work...
Doing work...
Doing work...
Doing work...
Doing work...
Note that Test2 itself is only called once, but the enumerable is iterated twice (and the work is done twice!).
You can avoid this by writing:
var items = Test2().ToList();
Which will immediately evaluate the enumerable and put it into a list. In this case, the work is only done once.
As many pointed out, the purpose of this warning is to point out that an expensive operation may be happening more than once. This happens because ReSharper sees that your method returns a IEnumerable which could lead to lazy evaluation, if you where using yield returns or most LINQ methods.
ReSharper stops warning about multiple evaluation when it can know for sure that the thing you are iterating over is a collection. You can provide that information to ReSharper in 2 ways.
Change the return type of Test2 to IList<string>
Before the first
foreach add System.Diagnostics.Debug.Assert(items is
IList<string>);
If you use ToList() over the returned IEnumerable<string> ReSharper will also know that you are iterating over a collection, but you would also be creating an unnecessary temporary list (you already had an array), paying the cost of time and memory to build that new list.
IEnumerable<T> is an interface that has an enumerator that will be called every time you want to access your collection of data (the foreach loops). Resharper warns you that if your data is not ordered, and you call this enumerator on the dataset multiple times, then the runtime will probably need to go through your collection multiple times which can put load and slow down execution time.
In order to avoid that you can cast your dataset to an ordered collection first: e.g. call .ToArray() or .ToList() on your items variable.
This question already has answers here:
Merge two (or more) lists into one, in C# .NET
(14 answers)
Create a list from two object lists with linq
(8 answers)
Closed 9 years ago.
I have an asp.net application where I have 4 List collection objects
List<string> sTransID = new List<string>();
List<string> sOriginID = new List<string>();
List<string> sDestID = new List<string>();
List<string> sCourierID = new List<string>();
These objects are populated at different sections of the application (inside a class, or an aspx code behind etc). The page performance is significantly slow when List elements size increase.
What would be the fastest way to loop through these objects when reading their values (in order to avoid having to loop through 4 objects) ? Can I merge these objects into a parent List object and loop through the parent?
Update:
By merge, I mean something like:
var Parent = List<sTransID>, List<sOriginID>, List<sDestID>, List<sCourierID>
var GetLocation = Parent[0]; // will return TransID[0], Origin[0], DestID[0], CourierID[0]
I have an Extension method that merges dictionaries. It might be of some help if you modify it for Lists.
public static class DictionaryExtensions
{
public static T MergeLeft<T, K, V>(this T me, params IDictionary<K, V>[] others)
where T : IDictionary<K, V>, new()
{
var newMap = new T();
foreach (var p in (new List<IDictionary<K, V>> { me }).Concat(others).SelectMany(src => src))
{
newMap[p.Key] = p.Value;
}
return newMap;
}
}
used as follows:
var mergedDictionary = new Dictionary<string, string>().MergeLeft(dic1, dic2, dic3);
One way would be to just concat the lists together before looping:
var itemsToLoop = sTransID.Concat(sOriginID).Concat(sDestID).Concat(sCourierID);
If I want an empty enumeration, I can call Enumerable.Empty<T>(). But what if I want to convert a scalar type to an enumeration?
Normally I'd write new List<string> {myString} to pass myString to a function that accepts IEnumerable<string>. Is there a more LINQ-y way?
You can use Repeat:
var justOne = Enumerable.Repeat(value, 1);
Or just an array of course:
var singleElementArray = new[] { value };
The array version is mutable of course, whereas Enumerable.Repeat isn't.
Perhaps the shortest form is
var sequence = new[] { value };
There is, but it's less efficient than using a List or Array:
// an enumeration containing only the number 13.
var oneIntEnumeration = Enumerable.Repeat(13, 1);
You can also write your own extension method:
public static class Extensions
{
public static IEnumerable<T> AsEnumerable<T>(this T item)
{
yield return item;
}
}
Now I haven't done that, and now that I know about Enumerable.Repeat, I probably never will (learn something new every day). But I have done this:
public static IEnumerable<T> MakeEnumerable<T>(params T[] items)
{
return items;
}
And this, of course, works if you call it with a single argument. But maybe there's something like this in the framework already, that I haven't discovered yet.