2

Let's say I have the following variables:

byte[] fileData;
List<byte> bundleData;

and I'd like to take a contiguous portion of fileData and add it to bundleData. My current method is essentially the following:

int startIndex = 20, endIndex = 80;
byte[] transferredData = new byte[endIndex - startIndex];
Array.Copy(fileData, startIndex, transferredData, 0, transferredData.Length);
bundleData.AddRange(transferredData);

Creating the intermediate array works fine, but it uses what seems like an unnecessary copy. Is there any way to add the data directly, without the use of a redundant array?

For reference, I'm using .NET 2.0 on this project.

4
  • No, I guess not. You will have to create an intermediate array. Commented Jul 16, 2013 at 16:12
  • That's unfortunate. I figured as much, since it's probably too much of a special case for it to be included. It just seems odd that there is no overload of AddRange that adds only a portion of a collection, so I hoped for an equivalent somewhere. Commented Jul 16, 2013 at 16:14
  • 1
    Have you considered using the MemoryStream class instead of a List<T>? Commented Jul 16, 2013 at 16:29
  • That would work well for my current use case. Care to edit it into your answer, so that people could see all three ways easily? Commented Jul 16, 2013 at 16:35

4 Answers 4

4

Another (probably efficient) way would be to use LINQ:

bundleData.AddRange(fileData.Skip(startIndex).Take(endIndex - startIndex));
Sign up to request clarification or add additional context in comments.

Comments

2

The List<T> class essentially just wraps a T array that is replaced with a larger T array when the array is full. The fastest way to append your byte array to List<byte> would be to copy the bytes directly into the internal byte array.

However, the List<T> class does not expose the internal array, so the best option is probably to ensure that the list has enough capacity and does not need to replace the internal array multiple times, and then add each byte one by one:

bundleData.Capacity += endIndex - startIndex + 1;

for (int i = startIndex; i <= endIndex; i++)
{
    bundleData.Add(fileData[i]);
}

You could also experiment with AddRange and providing a view of the byte array:

static IEnumerable<T> Range<T>(this T[] array, int offset, int count)
{
    for (int i = 0; i < count; i++)
    {
        yield return array[offset + i];
    }
}

bundleData.AddRange(fileData.Range(startIndex, endIndex - startIndex + 1));

5 Comments

Better way is creating Intermediate array only since it is added bulk at one shot, both of your methods will perform a LOOP
I mainly wanted to show more solutions to perform the task. The OP should use a profiler to determine which solution provides the best performance.
Interesting ideas. They do appear to solve the memory issue, as long as I expand the List first. I'll run some tests when I get the chance to see how the performance varies between the methods.
Yes you can Benchmark and test performance with variable number of data in Array.
Adjusting bundleData.Capacity will ruin the exponential growth strategy of the list, causing the algorithm to take quadratic time rather than amortised linear time. Plus, as written, it will grow the list even if it actually has capacity already. The other part of the question, looping over individual bytes, looks like it could be too slow.
1

If you do not actually need a List<byte> instance, a better option might be the MemoryStream class:

MemoryStream Class

Creates a stream whose backing store is memory.

Example:

MemoryStream bundleData = new MemoryStream();
bundleData.Write(fileData, startIndex, endIndex - startIndex + 1);

2 Comments

For those who this doesn't work for, dtb has another answer with other possible (though profiling is needed) solutions.
The link shown in the answer is deprecated; the current / maintained link is here: msdn.microsoft.com/en-us/library/…
1

List.AddRange method is implemented as below. I'll add some pseudo code to explain.

ICollection<T> is2 = collection as ICollection<T>;
if(is2!=null)
{
   //then use Array.Copy
}
else
{
 //Loop through GetEnumerator() and calls Insert()
}

So, Intermediate array will be the best idea IMHO since Array Implements ICollection. Hope this helps.

3 Comments

But that only works if you're adding the entire array. The OP wants to add just a portion of the array to the list.
@JimMischel yes but Creating a copy of that portion and passing it into AddRange() is better than looping and saying Add() right? assuming array size may grow long.
"Better in what way?" As dtb pointed out in his answer, you can pre-allocate the list by setting the Capacity property. And creating a copy of the subrange to be added is explicitly what the OP wants to avoid. Given that constraint and his use of .NET 2.0, the only reasonable solution is the loop.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.