Как удалить объекты из массива в Java?
дан массив n объекты, скажем, это массив строк, и имеет следующие значения:
foo[0] = "a";
foo[1] = "cc";
foo[2] = "a";
foo[3] = "dd";
что мне нужно сделать, чтобы удалить / удалить все строки / объекты, равные "а" в массиве?
18 ответов
[Если вам нужен готовый код, перейдите к моему "Edit3" (после разреза). Остальное здесь для потомков.]
воплощение идея мусорщика:
List<String> list = new ArrayList<String>(Arrays.asList(array));
list.removeAll(Arrays.asList("a"));
array = list.toArray(array);
Edit: теперь я использую Arrays.asList
вместо Collections.singleton
: синглтон ограничен одной записью, тогда как asList
подход позволяет добавлять другие строки для фильтрации позже:Arrays.asList("a", "b", "c")
.
Edit2: вышеуказанный подход сохраняет тот же массив (таким образом, массив все тот же length); элемент после последнего имеет значение null. Если вы хотите новая массив размером точно по мере необходимости, используйте это вместо:
array = list.toArray(new String[0]);
Edit3: если вы часто используете этот код в одном классе, возможно, вы захотите добавить это в свой класс:
private static final String[] EMPTY_STRING_ARRAY = new String[0];
затем функция будет:
List<String> list = new ArrayList<>();
Collections.addAll(list, array);
list.removeAll(Arrays.asList("a"));
array = list.toArray(EMPTY_STRING_ARRAY);
это прекратит засорять вашу кучу бесполезными пустыми строковыми массивами, которые в противном случае были бы new
ed каждый время вызова вашей функции.
предложение циника (см. комментарии) также поможет с мусором кучи, и для справедливости я должен упомянуть об этом:
array = list.toArray(new String[list.size()]);
Я предпочитаю свой подход, потому что может быть проще получить явный размер неправильно (например, вызов size()
в том списке).
альтернатива в Java 8:
String[] filteredArray = Arrays.stream(array)
.filter(e -> !e.equals(foo)).toArray(String[]::new);
сделать List
из массива с Arrays.asList()
, а вызов remove()
по всем соответствующим элементам. Тогда звоните toArray()
в "списке", чтобы снова вернуться в массив.
не очень эффективно, но если вы инкапсулируете его должным образом, вы всегда можете сделать что-то быстрее позже.
вы всегда можете это сделать:
int i, j;
for (i = j = 0; j < foo.length; ++j)
if (!"a".equals(foo[j])) foo[i++] = foo[j];
foo = Arrays.copyOf(foo, i);
вы можете использовать внешние библиотеки:
org.apache.commons.lang.ArrayUtils.remove(java.lang.Object[] array, int index)
это в проекте Apache Commons langhttp://commons.apache.org/lang/
см. код ниже
ArrayList<String> a = new ArrayList<>(Arrays.asList(strings));
a.remove(i);
strings = new String[a.size()];
a.toArray(strings);
Если вам нужно удалить несколько элементов из массива без преобразования его в List
ни создание дополнительного массива, вы можете сделать это в O (n) не зависит от количества элементов для удаления.
здесь a
- это исходный массив, int... r
различные упорядоченные индексы (позиции) элементов для удаления:
public int removeItems(Object[] a, int... r) {
int shift = 0;
for (int i = 0; i < a.length; i++) {
if (shift < r.length && i == r[shift]) // i-th item needs to be removed
shift++; // increment `shift`
else
a[i - shift] = a[i]; // move i-th item `shift` positions left
}
for (int i = a.length - shift; i < a.length; i++)
a[i] = null; // replace remaining items by nulls
return a.length - shift; // return new "length"
}
небольшой тестирование:
String[] a = {"0", "1", "2", "3", "4"};
removeItems(a, 0, 3, 4); // remove 0-th, 3-rd and 4-th items
System.out.println(Arrays.asList(a)); // [1, 2, null, null, null]
в вашей задаче вы можете сначала сканировать массив, чтобы собрать позиции "a", а затем вызвать removeItems()
.
что-то о том, чтобы сделать список, а затем удалить, а затем вернуться к массиву, кажется мне неправильным. Не тестировали, но я думаю, что следующее будет работать лучше. Да, я, вероятно, неоправданно предварительно оптимизируюсь.
boolean [] deleteItem = new boolean[arr.length];
int size=0;
for(int i=0;i<arr.length;i==){
if(arr[i].equals("a")){
deleteItem[i]=true;
}
else{
deleteItem[i]=false;
size++;
}
}
String[] newArr=new String[size];
int index=0;
for(int i=0;i<arr.length;i++){
if(!deleteItem[i]){
newArr[index++]=arr[i];
}
}
Я понимаю, что это очень старый пост,но некоторые из ответов здесь помогли мне, так что вот мой двухпенсовик!
я боролся за то, чтобы это работало довольно долго, прежде чем ветвиться, что массив, в который я пишу, должен быть изменен, если только изменения не внесены в ArrayList
оставьте размер списка без изменений.
если ArrayList
то, что вы изменяете, заканчивается большим или меньшим количеством элементов, чем началось, строка List.toArray()
будет вызовите исключение, поэтому вам нужно что-то вроде List.toArray(new String[] {})
или List.toArray(new String[0])
для создания массива с новым (правильным) размером.
теперь, когда я это знаю, звучит очевидно. Не так очевидно для новичка Android/Java, который сталкивается с новыми и незнакомыми кодовыми конструкциями и не очевиден из некоторых предыдущих сообщений здесь, поэтому просто хотел сделать этот момент действительно ясным для кого-то еще, почесывая голову в течение нескольких часов, как я!
EDIT:
точка с нулями в массиве была очищена. Извините за мои комментарии.
Оригинал:
Эм... линия
array = list.toArray(array);
заменяет все пробелы в массиве, где удаленный элемент был с null. Это может быть опасно, потому что элементы удаляются, но длина массива остается неизменным!
если вы хотите избежать этого, используйте новый массив в качестве параметра для toArray (). Если вы не хотите использовать removeAll, набор будет альтернативой:
String[] array = new String[] { "a", "bc" ,"dc" ,"a", "ef" };
System.out.println(Arrays.toString(array));
Set<String> asSet = new HashSet<String>(Arrays.asList(array));
asSet.remove("a");
array = asSet.toArray(new String[] {});
System.out.println(Arrays.toString(array));
выдает:
[a, bc, dc, a, ef]
[dc, ef, bc]
, где в качестве текущего принимается ответ от Криса прошлый молодой выходы:
[a, bc, dc, a, ef]
[bc, dc, ef, null, ef]
код
String[] array = new String[] { "a", "bc" ,"dc" ,"a", "ef" };
System.out.println(Arrays.toString(array));
List<String> list = new ArrayList<String>(Arrays.asList(array));
list.removeAll(Arrays.asList("a"));
array = list.toArray(array);
System.out.println(Arrays.toString(array));
без какого-либо значения null оставил.
мой маленький вклад в эту проблему.
public class DeleteElementFromArray {
public static String foo[] = {"a","cc","a","dd"};
public static String search = "a";
public static void main(String[] args) {
long stop = 0;
long time = 0;
long start = 0;
System.out.println("Searched value in Array is: "+search);
System.out.println("foo length before is: "+foo.length);
for(int i=0;i<foo.length;i++){ System.out.println("foo["+i+"] = "+foo[i]);}
System.out.println("==============================================================");
start = System.nanoTime();
foo = removeElementfromArray(search, foo);
stop = System.nanoTime();
time = stop - start;
System.out.println("Equal search took in nano seconds = "+time);
System.out.println("==========================================================");
for(int i=0;i<foo.length;i++){ System.out.println("foo["+i+"] = "+foo[i]);}
}
public static String[] removeElementfromArray( String toSearchfor, String arr[] ){
int i = 0;
int t = 0;
String tmp1[] = new String[arr.length];
for(;i<arr.length;i++){
if(arr[i] == toSearchfor){
i++;
}
tmp1[t] = arr[i];
t++;
}
String tmp2[] = new String[arr.length-t];
System.arraycopy(tmp1, 0, tmp2, 0, tmp2.length);
arr = tmp2; tmp1 = null; tmp2 = null;
return arr;
}
}
здесь много ответов-проблема, как я вижу, заключается в том, что вы не сказали, почему вы используете массив вместо коллекции, поэтому позвольте мне предложить пару причин и какие решения будут применяться (большинство решений уже ответили на другие вопросы здесь, поэтому я не буду вдаваться в подробности):
причина: Вы не знали о существовании пакета коллекции или не доверяли ему
решение: используйте коллекцию.
Если вы планируете добавлять / удалять из середины, используйте LinkedList. Если вы действительно беспокоитесь о размере или часто индексируете прямо в середину коллекции, используйте ArrayList. Оба они должны иметь операции удаления.
причина: вы обеспокоены размером или хотите контролировать распределение памяти
решение: используйте ArrayList с определенным начальным размером.
ArrayList-это просто массив, который может расширяться сам по себе, но это не всегда нужно делать. Это будет очень умно о добавлении / удалении элементов, но опять же, если вы вставляете/удаляете много из середины, используйте LinkedList.
причина: у вас есть массив, входящий и массив, выходящий-поэтому вы хотите работать с массивом
решение: преобразовать его в ArrayList, удалить элемент и преобразовать его обратно
причина: вы думаете, что можете написать лучший код, если вы это сделаете себя!--5-->
решение: вы не можете использовать массив или связанный список.
причина: это назначение класса, и вы не разрешены или у вас нет доступа к API коллекции по какой-либо причине
предположение: вам нужен новый массив, чтобы быть правильным "размером"
решение: Просканируйте массив на предмет совпадения элементов и подсчитайте их. Создайте новый массив правильного размера (исходный размер - количество совпадений). использование системы.arraycopy неоднократно копировать каждую группу элементов, которые вы хотите сохранить в новый массив. Если это назначение класса, и вы не можете использовать System.arraycopy, просто скопируйте их по одному вручную в цикле, но никогда не делайте этого в производственном коде, потому что это намного медленнее. (Эти решения подробно описаны в других ответах)
причина: вам нужно запустить голый металл
предположение: вы не должны выделять пространство без необходимости или принимать слишком долго
предположение: вы отслеживаете размер, используемый в массиве (длина) отдельно, потому что в противном случае вам придется перераспределить массив для удаления/вставки.
пример того, почему вы можете это сделать: один массив примитивов (скажем, значения int) занимает значительный кусок вашей ОЗУ-например, 50%! ArrayList заставит их в список указателей на целочисленные объекты, которые будут использовать в несколько раз больше памяти.
решение: Повторите свой массив и всякий раз, когда вы найдете элемент для удаления (назовем его элементом n), используйте System.arraycopy для копирования хвоста массива над "удаленным" элементом (источник и назначение-один и тот же массив) - он достаточно умен, чтобы сделать копию в правильном направлении, чтобы память не перезаписывала себя:
System.arraycopy(ary, n+1, ary, n, length-n) length--;
вы, вероятно, захотите быть умнее, если вы удаляете более одного элемента за раз. Вы только переместите область между одним "совпадением" и следующий, а не весь хвост и, как всегда, избегайте перемещения любого куска дважды.
в этом последнем случае вы абсолютно должны выполнить работу самостоятельно и с помощью системы.arraycopy действительно единственный способ сделать это, так как он собирается выбрать лучший способ перемещения памяти для вашей компьютерной архитектуры-это должно быть во много раз быстрее, чем любой код, который вы могли бы разумно написать сами.
Это зависит от того, что вы подразумеваете под "удалить"? Массив - это конструкция фиксированного размера-вы не можете изменить количество элементов в ней. Таким образом, вы можете либо a) создать новый, более короткий массив без элементов, которые вам не нужны, либо b) назначить записи, которые вы не хотите, чему-то, что указывает на их "пустой" статус; обычно null, если вы не работаете с примитивами.
в первом случае создайте список из массива, удалите элементы и создайте новый массив из списка. Если производительность важна итерация по массиву, назначая любые элементы, которые не должны быть удалены в список, а затем создайте новый массив из списка. Во втором случае просто пройдите и назначьте null для записей массива.
Arrgh, я не могу получить код, чтобы показать правильно. Извини, у меня получилось. Извините еще раз, я не думаю, что правильно понял вопрос.
String foo[] = {"a","cc","a","dd"},
remove = "a";
boolean gaps[] = new boolean[foo.length];
int newlength = 0;
for (int c = 0; c<foo.length; c++)
{
if (foo[c].equals(remove))
{
gaps[c] = true;
newlength++;
}
else
gaps[c] = false;
System.out.println(foo[c]);
}
String newString[] = new String[newlength];
System.out.println("");
for (int c1=0, c2=0; c1<foo.length; c1++)
{
if (!gaps[c1])
{
newString[c2] = foo[c1];
System.out.println(newString[c2]);
c2++;
}
}
будет копировать все элементы, кроме одного с индексом i:
if(i == 0){
System.arraycopy(edges, 1, copyEdge, 0, edges.length -1 );
}else{
System.arraycopy(edges, 0, copyEdge, 0, i );
System.arraycopy(edges, i+1, copyEdge, i, edges.length - (i+1) );
}
class sd
{
public static void main(String[ ] args)
{
System.out.println("Search and Delete");
int key;
System.out.println("Enter the length of array:");
Scanner in=new Scanner(System.in);
int n=in.nextInt();
int numbers[]=new int[n];
int i = 0;
boolean found = false;
System.out.println("Enter the elements in Array :");
for ( i = 0; i < numbers.length; i++)
{
numbers[i]=in.nextInt();
}
System.out.println("The elements in Array are:");
for ( i = 0; i < numbers.length; i++)
{
System.out.println(numbers[i]);
}
System.out.println("Enter the element to be searched:");
key=in.nextInt();
for ( i = 0; i < numbers.length; i++)
{
if (numbers[ i ] == key)
{
found = true;
break;
}
}
if (found)
{
System.out.println("Found " + key + " at index " + i + ".");
numbers[i]=0;//haven't deleted the element in array
System.out.println("After Deletion:");
for ( i = 0; i < numbers.length; i++)
{
if (numbers[ i ]!=0)
{ //it skips displaying element in array
System.out.println(numbers[i]);
}
}
}
else
{
System.out.println(key + "is not in this array.");
}
}
}//Sorry.. if there are mistakes.