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.