Dodaj nowy element do istniejącej tablicy w c # .net


134

Jak dodać nowy element do istniejącej tablicy ciągów w C # .net?

Muszę zachować istniejące dane.

Odpowiedzi:


120

Użyłbym listy, jeśli potrzebujesz tablicy o dynamicznym rozmiarze:

List<string> ls = new List<string>();
ls.Add("Hello");

28
A jeśli to konieczne, wykonaj na końcu ls.ToArray ()
Narayana

2
Korzystanie z innej mechaniki niż zadana w pytaniu nie jest odpowiedzią.
user11909

@ user2190035: Nie jestem pewien, skąd wziął się ten pomysł, a 111 osób, które zagłosowały za, nie zgodziłoby się z tobą. Jeśli znasz lepszy sposób na rozszerzenie tablicy, koniecznie opublikuj go. Wątpię jednak, aby ponowne przydzielenie i ręczne kopiowanie były lepsze niż korzystanie z listy. Czasami odpowiedź brzmi „nie rób tego”.
Ed S.

@ EdS. Pytanie brzmiało, jak dodać element do istniejącej tablicy ciągów , ale ta odpowiedź nie ma żadnej tablicy, zamiast tego tworzy nową List <>. W mojej aplikacji nie mogę zmienić typu na List <>, dlatego muszę zmienić rozmiar tablicy (zrobić kopię ...). Pomogła mi odpowiedź Ali Ersöza.
user11909

1
Dlaczego na świecie miałbyś to robić? Gdybyś musiał, zasugerowałbym Concat ()
Ed S.

102

To mogłoby być rozwiązanie;

Array.Resize(ref array, newsize);
array[newsize - 1] = "newvalue"

Ale dla tablicy o dynamicznym rozmiarze wolałbym też listę.


@Konrad, to z pewnością zachowa dane w tablicy.
Ali Ersöz

1
nie nadaje się to dłużej niż kilka razy. ponieważ funkcja „Resize” ma ogromną wydajność. błąd przez jeden lub dwa razy, to jest bardzo dobre.
reklama

54

Korzystanie z LINQ:

arr = (arr ?? Enumerable.Empty<string>()).Concat(new[] { newitem }).ToArray();

Lubię tego używać, ponieważ jest to jednolinijkowe i bardzo wygodne do osadzenia w instrukcji switch, prostej instrukcji if lub przekazaniu jako argumentu.

EDYTOWAĆ:

Niektórzy ludzie nie lubią, new[] { newitem }ponieważ tworzy małą, jednopozycyjną, tymczasową tablicę. Oto wersja używająca Enumerable.Repeat, która nie wymaga tworzenia żadnego obiektu (przynajmniej nie na powierzchni - iteratory .NET prawdopodobnie tworzą kilka obiektów automatu stanowego pod tabelą).

arr = (arr ?? Enumerable.Empty<string>()).Concat(Enumerable.Repeat(newitem,1)).ToArray();

A jeśli masz pewność, że tablica nigdy nullsię nie zaczyna, możesz ją uprościć, aby:

arr.Concat(Enumerable.Repeat(newitem,1)).ToArray();

Zauważ, że jeśli chcesz dodać elementy do uporządkowanej kolekcji, Listprawdopodobnie jest to pożądana struktura danych, a nie tablica, od której należy zacząć.


1
Bardzo ładne +1. Mam podobny kod jak ogólna metoda rozszerzenia. W tej odpowiedzi
zawarłem

Jest to bardzo przydatne, gdy szukałem „jak dołączyć do tablicy w jednej linii kodu w języku c #” - mam nadzieję, że ten komentarz wystarczy, aby następnym razem go znaleźć.
gary

28

Bardzo stare pytanie, ale nadal chciałem to dodać.

Jeśli szukasz jednej linijki, możesz użyć poniższego kodu. Łączy konstruktor listy, który akceptuje wyliczalną i "nową" (od czasu postawienia pytania) składnię inicjalizatora.

myArray = new List<string>(myArray) { "add this" }.ToArray();

27
 Array.Resize(ref youur_array_name, your_array_name.Length + 1);
 your_array_name[your_array_name.Length - 1] = "new item";

27

Tablice w C # są niezmienne , np string[],int[] . Oznacza to, że nie możesz zmienić ich rozmiaru. Musisz stworzyć zupełnie nową tablicę.

Oto kod dla Array.Resize :

public static void Resize<T>(ref T[] array, int newSize)
{
    if (newSize < 0)
    {
        throw new ArgumentOutOfRangeException("newSize", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }
    T[] sourceArray = array;
    if (sourceArray == null)
    {
        array = new T[newSize];
    }
    else if (sourceArray.Length != newSize)
    {
        T[] destinationArray = new T[newSize];
        Copy(sourceArray, 0, destinationArray, 0, (sourceArray.Length > newSize) ? newSize : sourceArray.Length);
        array = destinationArray;
    }
}

Jak widać, tworzy nową tablicę z nowym rozmiarem, kopiuje zawartość tablicy źródłowej i ustawia odwołanie do nowej tablicy. Wskazówka do tego jest ref słowo kluczowe dla pierwszego parametru.

Istnieją listy, które mogą dynamicznie przydzielać nowe miejsca na nowe przedmioty. To jest np. List <T> . Zawierają one niezmienne tablice i zmieniają ich rozmiar w razie potrzeby (List <T> nie jest połączoną implementacją listy!). ArrayList to to samo bez typów generycznych (z tablicą Object ).

LinkedList <T> to prawdziwa implementacja listy połączonej. Niestety możesz dodać tylko elementy LinkListNode <T> do listy, więc musisz zawinąć własne elementy listy w ten typ węzła. Myślę, że jego użycie jest rzadkie.


Myślę, że miałeś na myśli Array.Copy
user123456

2
Jedyna odpowiedź, która dostarcza informacji, dlaczego jest to niemożliwe i nie tylko sugeruje skorzystanie z listy…
Gilad Green

Uważam, że Aray.Resizemożna zmienić rozmiar tablicy bez utraty jej zawartości. docs.microsoft.com/en-us/dotnet/api/ ...
AbdelAziz AbdelLatef

8

Możesz rozwinąć odpowiedź udzieloną przez @Stephen Chung, używając jego logiki opartej na LINQ, aby utworzyć metodę rozszerzenia przy użyciu typu ogólnego.

public static class CollectionHelper
{
    public static IEnumerable<T> Add<T>(this IEnumerable<T> sequence, T item)
    {
        return (sequence ?? Enumerable.Empty<T>()).Concat(new[] { item });
    }

    public static T[] AddRangeToArray<T>(this T[] sequence, T[] items)
    {
        return (sequence ?? Enumerable.Empty<T>()).Concat(items).ToArray();
    }

    public static T[] AddToArray<T>(this T[] sequence, T item)
    {
        return Add(sequence, item).ToArray();
    }

}

Następnie możesz wywołać go bezpośrednio w tablicy w ten sposób.

    public void AddToArray(string[] options)
    {
        // Add one item
        options = options.AddToArray("New Item");

        // Add a 
        options = options.AddRangeToArray(new string[] { "one", "two", "three" });

        // Do stuff...
    }

Trzeba przyznać, że metoda AddRangeToArray () wydaje się nieco przesadzona, ponieważ masz tę samą funkcjonalność z Concat (), ale w ten sposób kod końcowy może „działać” bezpośrednio z tablicą, w przeciwieństwie do tego:

options = options.Concat(new string[] { "one", "two", "three" }).ToArray();

Dziękuję, to było bardzo pomocne, dodałem opcję usunięcia pozycji (mam nadzieję, że z tobą jest w porządku).
Tal Segal

@TalSegal nie ma za co, miło mi. Użyj kodu według własnego uznania!
dblood

6

Lepiej jest zachować niezmienny i stały rozmiar tablicy.

możesz symulować Addprzez Extension MethodiIEnumerable.Concat()

public static class ArrayExtensions
    {
        public static string[] Add(this string[] array, string item)
        {
            return array.Concat(new[] {item}).ToArray();
        }
    }

6

Więc jeśli masz istniejącą tablicę, moją szybką poprawką będzie

var tempList = originalArray.ToList();
tempList.Add(newitem);

Teraz po prostu zastąp oryginalną tablicę nową

originalArray = tempList.ToArray();

Rozwiązałem moje rozwiązanie
praguan

5

jeśli z jakiegoś powodu dużo pracujesz z tablicami, a nie z listami, ta ogólna metoda zwracania typowego powrotu Addmoże pomóc

    public static T[] Add<T>(T[] array, T item)
    {
        T[] returnarray = new T[array.Length + 1];
        for (int i = 0; i < array.Length; i++)
        {
            returnarray[i] = array[i];
        }
        returnarray[array.Length] = item;
        return returnarray;
    }

5

Dodano nową Append<TSource>metodęIEnumerable<TSource> od .NET Framework 4.7.1 i .NET Core 1.0.

Oto jak z niego korzystać:

var numbers = new [] { "one", "two", "three" };
numbers = numbers.Append("four").ToArray();
Console.WriteLine(string.Join(", ", numbers)); // one, two, three, four

Zauważ, że jeśli chcesz dodać element na początku tablicy, możesz Prepend<TSource>zamiast tego użyć nowej metody.


4

Wszystkie proponowane odpowiedzi działają tak samo, jak to, czego chcieliby uniknąć, tworząc nową tablicę i dodając do niej nowy wpis tylko z większą stratą narzutu. LINQ nie jest magią, lista T to tablica z obszarem buforu z dodatkową przestrzenią, aby uniknąć zmiany rozmiaru tablicy wewnętrznej po dodaniu elementów.

Wszystkie abstrakcje muszą rozwiązać ten sam problem, utworzyć tablicę bez pustych gniazd, które przechowują wszystkie wartości i zwrócić je.

Jeśli potrzebujesz elastyczności i możesz utworzyć wystarczająco dużą listę, której możesz użyć, aby ją przekazać, zrób to. w przeciwnym razie użyj tablicy i udostępnij ten bezpieczny wątkowo obiekt. Ponadto nowy Span pomaga udostępniać dane bez konieczności kopiowania list.

Aby odpowiedzieć na pytanie:

Array.Resize(ref myArray, myArray.Length + 1);
data[myArray.Length - 1] = Value;

2

Korzystanie z listy byłoby najlepszą opcją do zarządzania pamięcią.


2

A co z użyciem metody rozszerzenia? Na przykład:

public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> source, TSource item)
{
    return source.Union(new TSource[] { item });
}

na przykład:

string[] sourceArray = new []
{
    "foo",
    "bar"
}
string additionalItem = "foobar";
string result = sourceArray.Union(additionalItem);

Zauważ, że naśladuje to zachowanie rozszerzenia Uniion Linq (używanego do łączenia dwóch tablic w nową) i wymaga do działania biblioteki Linq.


1

Zgadzam się z Edem. C # nie czyni tego tak prostym, jak robi to VB z ReDim Preserve. Bez kolekcji musisz skopiować tablicę do większej.


2
Dzięki Bogu! ReDim działa niezwykle wolno, gdy jest nadużywany. =)
Ed S.

ReDim Preservepo prostu kopiuje tablicę do większej. Nie ma cudownej zmiany rozmiaru tablicy.
Olivier Jacot-Descombes

1
string str = "string ";
List<string> li_str = new List<string>();
    for (int k = 0; k < 100; i++ )
         li_str.Add(str+k.ToString());
string[] arr_str = li_str.ToArray();

0
private static string[] GetMergedArray(string[] originalArray, string[] newArray)
    {
        int startIndexForNewArray = originalArray.Length;
        Array.Resize<string>(ref originalArray, originalArray.Length + newArray.Length);
        newArray.CopyTo(originalArray, startIndexForNewArray);
        return originalArray;
    }


0

Niestety korzystanie z listy nie zadziała we wszystkich sytuacjach. Lista i tablica są w rzeczywistości różne i nie są w 100% zamienne. Zależałoby od okoliczności, czy byłoby to dopuszczalne rozwiązanie.


0

Ponieważ to pytanie nie jest zadowalające z udzielonej odpowiedzi, chciałbym dodać tę odpowiedź :)

public class CustomArrayList<T> 
 {  
   private T[] arr;  private int count;  

public int Count  
  {   
    get   
      {    
        return this.count;   
      }  
   }  
 private const int INITIAL_CAPACITY = 4;  

 public CustomArrayList(int capacity = INITIAL_CAPACITY) 
 {  
    this.arr = new T[capacity];   this.count = 0; 
  } 

 public void Add(T item) 
  {  
    GrowIfArrIsFull();  
   this.arr[this.count] = item;  this.count++; 
  }  

public void Insert(int index, T item) 
{  
 if (index > this.count || index < 0)  
    {   
      throw new IndexOutOfRangeException(    "Invalid index: " + index);  
     }  
     GrowIfArrIsFull();  
     Array.Copy(this.arr, index,   this.arr, index + 1, this.count - index);          
    this.arr[index] = item;  this.count++; }  

    private void GrowIfArrIsFull() 
    {  
    if (this.count + 1 > this.arr.Length)  
    {   
      T[] extendedArr = new T[this.arr.Length * 2];  
      Array.Copy(this.arr, extendedArr, this.count);  
      this.arr = extendedArr;  
    } 
  }
 }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.