3

I'm trying to write a method which accepts a List<string> and then converts the entire list into one big array of bytes. Like this:

private byte[] ConvertStringsToBytes(List<string> list)
{
    List<byte> byteList = new List<byte>();

    foreach (var i in list)
    {
        byteList.Add(Encoding.UTF8.GetBytes(i));
    }

    return byteList.ToArray();
}

However I get:

Argument type 'byte[]' is not assignable to paramter type 'byte' on the byteList.Add(Encoding.UTF8.GetBytes(i));

Where am I going wrong? How do I correctly turn this list into one byte array?

1
  • 7
    Use AddRange(), not Add Commented Aug 13, 2015 at 7:32

4 Answers 4

7

A more efficient way would be to first join the strings together and then convert it into an byte array like this:

List<string> input = new List<string> { "first", "second" };
string fullString = String.Join(String.Empty, list.ToArray());
byte[] byteArray = Encoding.UTF8.GetBytes(fullString);

If performance matters and you have a lot of strings in that list you would like this way: Edit: After benchmarking, this method is indeed slower than the above.

List<string> input = new List<string> { "first", "second" };
StringBuilder sb = new StringBuilder();
foreach (string s in input )
    sb.Append(s);

byte[] byteArray = Encoding.UTF8.GetBytes(sb.ToString());

Edit: Did some benchmarking of some of the methods mentioned in this post. Here is the output for a release build:

ConvertWithString         896ms
ConvertWithStringBuilder  858ms
ConvertWithConcat        1529ms
ConvertWithSelectMany    2234ms
ConvertWithBuffer         904ms

ConvertWithString         501ms
ConvertWithStringBuilder  919ms
ConvertWithConcat        1435ms
ConvertWithSelectMany    2044ms
ConvertWithBuffer         636ms

Looks like performance does not really matter if you don't have a lot of strings.

And here's the code:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace ConsoleApplication2
{
    internal class Program
    {
        static byte[] ConvertWithBuffer(List<string> list)
        {
            int totalSize = list.Sum(x => Encoding.UTF8.GetByteCount(x));
            byte[] buffer = new byte[totalSize];

            int ix = 0;

            foreach (string str in list)
                ix += Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, ix);

            return buffer;
        }

        static byte[] ConvertWithConcat(List<string> list) { return Encoding.UTF8.GetBytes(String.Concat(list)); }

        static byte[] ConvertWithSelectMany(List<string> list)
        {
            return list
                .SelectMany(line => Encoding.UTF8.GetBytes(line))
                .ToArray();
        }

        static byte[] ConvertWithString(List<string> input)
        {
            string fullString = String.Join(String.Empty, input.ToArray());
            return Encoding.UTF8.GetBytes(fullString);
        }

        static byte[] ConvertWithStringBuilder(List<string> input)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string s in input)
                sb.Append(s);

            return Encoding.UTF8.GetBytes(sb.ToString());
        }

        static IEnumerable<string> CreateList()
        {
            for (int i = 0; i < 10000000; i++)
                yield return i.ToString();
        }

        static void Main(string[] args)
        {
            List<string> strings = CreateList().ToList();
            Stopwatch stopWatch = Stopwatch.StartNew();

            // warm up
            ConvertWithString(strings);
            ConvertWithStringBuilder(strings);
            ConvertWithConcat(strings);
            ConvertWithSelectMany(strings);
            ConvertWithBuffer(strings);

            // testing

            stopWatch.Restart();
            ConvertWithString(strings);
            Console.WriteLine("ConvertWithString {0}ms", stopWatch.ElapsedMilliseconds);

            stopWatch.Restart();
            ConvertWithStringBuilder(strings);
            Console.WriteLine("ConvertWithStringBuilder {0}ms", stopWatch.ElapsedMilliseconds);

            stopWatch.Restart();
            ConvertWithConcat(strings);
            Console.WriteLine("ConvertWithConcat {0}ms", stopWatch.ElapsedMilliseconds);

            stopWatch.Restart();
            ConvertWithSelectMany(strings);
            Console.WriteLine("ConvertWithSelectMany {0}ms", stopWatch.ElapsedMilliseconds);

            stopWatch.Restart();
            ConvertWithBuffer(strings);
            Console.WriteLine("ConvertWithBuffer {0}ms", stopWatch.ElapsedMilliseconds);

            Console.WriteLine("press any key...");
            Console.ReadKey();
        }
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

It'd be interesting to see wether your method is faster/slower/same as Dmitry Bychenko's solution.
I'm pretty sure it is. Take a look at xanatos solution, this might be woth benchmarking against the string builder.
Oh, do you really think that performance in his case really matters ??!!
Nope, like written above. But I was curious :) It matters only for really huge lists. For anything else I would pick the solution by it's readability.
3

Eh, something like this (Linq)?

private byte[] ConvertStringsToBytes(List<string> list) {
  return list
    .SelectMany(line => Encoding.UTF8.GetBytes(line))
    .ToArray();
}

Yet another possibility is

private byte[] ConvertStringsToBytes(List<string> list) {
  return Encoding.UTF8.GetBytes(String.Concat(list));
}

Comments

1

You could do something like this:

private static byte[] ConvertStringsToBytes(List<string> list)
{
    int totalSize = list.Sum(x => Encoding.UTF8.GetByteCount(x));
    byte[] buffer = new byte[totalSize];

    int ix = 0;

    foreach (string str in list)
    {
        ix += Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, ix);
    }

    return buffer;
}

I create a single big buffer by precalculating the total size needed (in totalSize) and then I fill it in the foreach cycle. Note the use of the ix variable to save the current position in buffer.

The advantage of this method over other methods is that there is no copying around of strings or byte arrays. The UTF8 encoded string is written exactly once in the buffer buffer and isn't copied around.

Comments

0

List.Add() lets you add only a single element. You need to put the GetBytes() result into an array, loop over the array and add each single array element to the list.
Maybe you find a way to convert the GetBytes array to a list and use AddRange()...

Alternatively you omit all List in you function and work with arrays only. You could use Array.Resize, but that's mit the best in means of performance, because this would include lots of value copying. You better use an array of array of byte, calculate the needed final byte array size and then copy the data if each inner array to your final array.

3 Comments

ToList() is your friend there.
@SteffenWinkler Is this plain c# or Linq or whatever?
ToList() is part of the Linq namespace, yes. (and it's pretty cool)

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.