is there in C# a method for List<T> like resize in c++ for vector<T>
Asked Answered
L

8

40

When I use resize(int newsize) in C++ for vector<T>, it means that the size of this vector are set to newsize and the indexes run in range [0..newsize). How to do the same in C# for List<T>?
Changing the List<T> property Capacity only changes the Capacity but leaves the Count the same, and furthermore the indexes still are in range [0..Count). Help me out, please.

P.S. Imagine I have a vector<T> tmp with a tmp.size() == 5 I cannot refer to tmp[9], but when I then use tmp.resize(10) I may refer to tmp[9]. In C# if I have List<T> tmp with tmp.Count == 5 I cannot refer to tmp[9] (IndexOutOfRangeException), but even when I set tmp.Capacity=10 I will not be able to refer to tmp[9] coz of tmp.Count is still 5. I want to find some analogy of resize in C#.

Lief answered 1/9, 2012 at 21:30 Comment(6)
just curious, why u wanna do that?Exhortative
@Mr.Anubis resize increases size, reserve increases capacity.Moro
Can you explain what you are trying to achieve? Why do you need this?Contretemps
"it means that the capacity and size of this vector are set to newsize" No it doesn't; only the size will be set. The capacity may or may not be set to that.Skewness
If you want to change the size (Count) of a .NET List, just add to or remove elements from it.Doom
@Doom that's what std::vector<T>::resize() does as a handy method for adding or removing the amount required. It's nothing you couldn't do yourself, but it is a convenience that that C++ class has, that the equivalent C# class does not.Ritz
R
45

No, but you can use extension methods to add your own. The following has the same behaviour as std::vector<T>::resize(), including the same time-complexity. The only difference is that in C++ we can define a default with void resize ( size_type sz, T c = T() ) and the way templates work means that that's fine if we call it without the default for a T that has no accessible parameterless constructor. In C# we can't do that, so instead we have to create one method with no constraint that matches the non-default-used case, and another with a where new() constraint that calls into it.

public static class ListExtra
{
    public static void Resize<T>(this List<T> list, int sz, T c)
    {
        int cur = list.Count;
        if(sz < cur)
            list.RemoveRange(sz, cur - sz);
        else if(sz > cur)
        {
            if(sz > list.Capacity)//this bit is purely an optimisation, to avoid multiple automatic capacity changes.
              list.Capacity = sz;
            list.AddRange(Enumerable.Repeat(c, sz - cur));
        }
    }
    public static void Resize<T>(this List<T> list, int sz) where T : new()
    {
        Resize(list, sz, new T());
    }
}

Now the likes of myList.Resize(23) or myList.Resize(23, myDefaultValue) will match what one expects from C++'s vector. I'd note though that sometimes where with C++ you'd have a vector of pointers, in C# you'd have a list of some reference-type. Hence in cases where the C++ T() produces a null pointer (because it's a pointer), here we're expecting it to call a parameterless constructor. For that reason you might find it closer to the behaviour you're used to to replace the second method with:

  public static void Resize<T>(this List<T> list, int sz)
  {
      Resize(list, sz, default(T));
  }

This has the same effect with value types (call parameterless constructor), but with reference-types, it'll fill with nulls. In which case, we can just rewrite the entire class to:

public static class ListExtra
{
    public static void Resize<T>(this List<T> list, int sz, T c = default(T))
    {
        int cur = list.Count;
        if(sz < cur)
            list.RemoveRange(sz, cur - sz);
        else if(sz > cur)
            list.AddRange(Enumerable.Repeat(c, sz - cur));
    }
}

Note that this isn't so much about differences between std::vector<T> and List<T> as about the differences in how pointers are used in C++ and C#.

Ritz answered 1/9, 2012 at 23:10 Comment(8)
do we really need list.Capacity = sz; Do not AddRange knows the size of adding Collection?Beale
@fantastory no, it's not needed, as indicated by the comment.Ritz
The question is, do we need it for optimization. Yes we need because AddRange does not know size of enumeration before enumerating it.Beale
Mind you the where T : new() version will cause any added elements to reference the same object... Probably better to loop the additions with while (list.Count < list.Capacity) { list.Add(new T()); } instead, since that's most likely the desired fuctionality..Trigger
@Trigger or perhaps better would be to have a form that takes a factory that can be called, with () -> new T() being given as a default.Ritz
@JonHanna ooh, hadn't thought of that - not a bad plan on account of being so flexible for constructors that might need arguments.Trigger
...further generic arguments required, though, if I'm not mistaken? More familiar with C++ templates than C# generics... miss my Args... :(Trigger
Nice. Though for me I would probably use null as the default if it's a reference type.Marketing
P
23

Just to make Jon Hanna's answer more readable:

public static class ListExtras
{
    //    list: List<T> to resize
    //    size: desired new size
    // element: default value to insert

    public static void Resize<T>(this List<T> list, int size, T element = default(T))
    {
        int count = list.Count;

        if (size < count)
        {
            list.RemoveRange(size, count - size);
        }
        else if (size > count)
        {
            if (size > list.Capacity)   // Optimization
                list.Capacity = size;

            list.AddRange(Enumerable.Repeat(element, size - count));
        }
    }
}
Paramorph answered 28/7, 2014 at 2:51 Comment(1)
if (size > list.Capacity) list.Capacity = size; This optimization is very bad. Consider example: for(int i = 0; i< 100000; i++) lst.Resize(i);Budd
S
2

As of .NET 8 instead of custom extension methods you can use CollectionsMarshal.SetCount<T>(List<T>, Int32) which supports both increasing and decreasing initialized list size and should be a bit more performant:

var ints = new List<int>();
CollectionsMarshal.SetCount(ints, 20);
Console.WriteLine(ints.Count); // prints 20
CollectionsMarshal.SetCount(ints, 10);
Console.WriteLine(ints.Count); // prints 10
Shout answered 23/1 at 20:17 Comment(0)
E
1

sorry. is this what u need? List.TrimExcess()

Exhortative answered 1/9, 2012 at 21:36 Comment(0)
D
1

This is my solution.

private void listResize<T>(List<T> list, int size)
{
   if (size > list.Count)
      while (size - list.Count > 0)
         list.Add(default<T>);    
   else if (size < list.Count)
      while (list.Count - size > 0)
         list.RemoveAt(list.Count-1);
}

When the size and list.Count are the same, there is no need to resize the list.

The default(T) parameter is used instead of null,"",0 or other nullable types, to fill an empty item in the list, because we don't know what type <T> is (reference, value, struct etc.).

P.S. I used for loops instead of while loops and i ran into a problem. Not always the size of the list was that i was asking for. It was smaller. Any thoughts why?

Check it:

private void listResize<T>(List<T> list, int size)
{
   if (size > list.Count)
      for (int i = 0; i <= size - list.Count; i++)
         list.Add(default(T));
   else if (size < list.Count)
      for (int i = 0; i <= list.Count - size; i++)
         list.RemoveAt(list.Count-1);
}
Darrick answered 28/5, 2014 at 6:40 Comment(0)
C
0

Setting List<T>.Capacity is like using std::vector<T>.reserve(..). Maybe List<T>.AddRange(..) fit your needs.

Castor answered 1/9, 2012 at 21:33 Comment(1)
Thnx, but I then will need to create the new range of items I want to add to the end of my current list :( I want to change only some integer value which represents the size of List<T>Lief
D
-2

Haven't you read at MSDN:-

A list is a resizable collection of items. Lists can be constructed multiple ways, but the most useful class is List. This allows you to strongly type your list, includes all of the essential functionality for dealing with collections, and can be easily searched.

Further:-

Capacity is the number of elements that the List can store before resizing is required, while Count is the number of elements that are actually in the List.

Capacity is always greater than or equal to Count. If Count exceeds Capacity while adding elements, the capacity is increased by automatically reallocating the internal array before copying the old elements and adding the new elements.

Dirac answered 1/9, 2012 at 21:37 Comment(2)
@novaco:- yes updated, see why you dont need to resize it... :)Dirac
Because while std::vector<T>::resize doesn't do anything you can't do by other means, if you're used to it, you'll certain miss it.Ritz
H
-8

A list doesn't have a finite size.

Is there a reason why the size matters to you?

Perhaps an array or a dictionary is closer to your requirements

Hulse answered 1/9, 2012 at 21:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.