为字符串数组添加新值的更好方法?

时间:2017-03-28 08:12:51

标签: c# arrays

我有以下代码:

private void AddMissingValue(ref string[] someArray) {
  string mightbemissing="Another Value";
  if (!someArray.Contains(mightbemissing)) {
    var lst=someArray.ToList();
    lst.Add(mightbemissing);
    someArray=lst.ToArray();
  }
}

虽然这有效(如果遗失的话,将一个项目添加到数组中),我想知道这是否可以以更智能的方式完成?我不喜欢将数组转换两次并为这么简单的任务编写这么多行。

有更好的方法吗?也许使用LinQ?

2 个答案:

答案 0 :(得分:7)

一般的想法是正确的 - 数组是固定大小的集合,如果不重新创建数组,则无法向其添加项目。

您可以使用LINQ .Concat方法以更优雅的方式编写您的方法,而无需创建List

private void AddMissingValue(ref string[] someArray)
{
    string mightbemissing = "Another Value";
    if (!someArray.Contains(mightbemissing))
    { 
        someArray = someArray.Concat(new[] { mightbemissing }).ToArray();
    }
}

此实现需要N * 2次操作,这比N * 3更好,但它仍然会多次枚举,并且在向数组添加N个项目时是二次方的。
如果您要经常执行此操作,那么更改代码以使用动态大小集合(f.i.,List)将是一种更有效的方法。

即使您决定继续使用数组,如果您返回修改后的数组而不是使用ref(imo)可能会更好看:

private string[] AddMissingValue(string[] someArray)
{
    string mightbemissing = "Another Value";
    return someArray.Contains(mightbemissing)
        ? someArray
        : someArray.Concat(new[] { mightbemissing }).ToArray();
}

// Example usage:

string[] yourInputArray = ...;
yourInputArray = AddMissingValue(yourInputArray);

LINQ风格和最高性能的

我想到的另一个实现,就性能而言是最好的(O(N))(不是针对动态大小的集合,而是针对以前的解决方案)并且是LINQ风格的:

public static class CollectionExtensions
{
    public static IEnumerable<T> AddIfNotExists<T>(this IEnumerable<T> enumerable, T value) 
    {
        bool itemExists = false;
        foreach (var item in enumerable)
        {
            if (!itemExists && value.Equals(item))
                itemExists = true;

            yield return item;
        }

        if (!itemExists)
            yield return value;
    }
}

// Example usage:
string[] arr = ...;
arr = arr.AddIfNotExists("Another Value").ToArray();

使用yield的此实现用于防止多次枚举。

如果你需要添加多个项目,那么它甚至可以通过这种方式重写,而且它似乎仍然是线性的:

public static IEnumerable<T> AddIfNotExists<T>(this IEnumerable<T> enumerable, params T[] value) 
{
    HashSet<T> notExistentItems = new HashSet<T>(value);
    foreach (var item in enumerable)
    {
        if (notExistentItems.Contains(item))
            notExistentItems.Remove(item);

        yield return item;
    }

    foreach (var notExistentItem in notExistentItems)
        yield return notExistentItem;
}

// Usage example:
int[] arr = new[] { 1, 2, 3 };
arr = arr.AddIfNotExists(2, 3, 4, 5).ToArray(); // 1, 2, 3, 4, 5

答案 1 :(得分:2)

您必须调整数组,请参阅

https://msdn.microsoft.com/en-us/library/bb348051(v=vs.110).aspx

了解详情。实现:

// static: it seems that you don't want "this" in the method
private static void AddMissingValue(ref string[] someArray) {
  string mightbemissing = "Another Value";

  if (!someArray.Contains(mightbemissing)) {
    Array.Resize(ref someArray, someArray.Length + 1);

    someArray[someArray.Length - 1] = mightbemissing;
  }
}

在您当前的实施中,您复制了所有项目两次,如果数组

,这可能是不需要的
...
var lst=someArray.ToList(); // first: all data copied from array to list
lst.Add(mightbemissing);
someArray=lst.ToArray();    // second: all data copied from list to array 

然而,更好的设计是从固定大小的数组string[]切换到List<string>

  List<string> someList = ...

  if (!someList.Contains(mightbemissing))
    someList.Add(mightbemissing); // <- just Add

如果所有值都不是null唯一,您可以进一步改进:

  HashSet<string> someHash = ...

  someHash.Add(mightbemissing);