¿Cómo elimino objetos de una matriz en Java?


82

Dada una matriz de n objetos, digamos que es una matriz de cadenas y tiene los siguientes valores:

foo[0] = "a";
foo[1] = "cc";
foo[2] = "a";
foo[3] = "dd";

¿Qué tengo que hacer para eliminar / eliminar todas las cadenas / objetos iguales a "a" en la matriz?


2
No puede cambiar el tamaño de una matriz en Java. Supongo que no desea anular los elementos, ya que sería trivial. ¿Quieres cambiar los otros elementos para eliminar los huecos?
Dan Dyer

1
Es trivial, ahora que sé que puedo hacerlo. ;) ¡Gracias!
Ramayac

Respuestas:


112

[Si desea algún código listo para usar, desplácese hasta mi "Edit3" (después del corte). El resto está aquí para la posteridad.]

Para desarrollar la idea de Dustman :

List<String> list = new ArrayList<String>(Arrays.asList(array));
list.removeAll(Arrays.asList("a"));
array = list.toArray(array);

Edit: Ahora estoy usando Arrays.asListen lugar de Collections.singleton: Singleton se limita a una entrada, mientras que el asListenfoque le permite añadir otras cadenas para filtrar más adelante: Arrays.asList("a", "b", "c").

Edit2: el enfoque anterior conserva la misma matriz (por lo que la matriz sigue teniendo la misma longitud); el elemento después del último se establece en nulo. Si desea una nueva matriz de tamaño exactamente como se requiere, use esto en su lugar:

array = list.toArray(new String[0]);

Edit3: si usa este código con frecuencia en la misma clase, es posible que desee considerar agregar esto a su clase:

private static final String[] EMPTY_STRING_ARRAY = new String[0];

Entonces la función se convierte en:

List<String> list = new ArrayList<>();
Collections.addAll(list, array);
list.removeAll(Arrays.asList("a"));
array = list.toArray(EMPTY_STRING_ARRAY);

Esto dejará de ensuciar su montón con matrices de cadenas vacías inútiles que de otro modo se editarían newcada vez que se llame a su función.

La sugerencia de Cynicalman (ver comentarios) también ayudará con el montón de basura, y para ser justos, debería mencionarlo:

array = list.toArray(new String[list.size()]);

Prefiero mi enfoque, porque puede ser más fácil obtener el tamaño explícito incorrecto (por ejemplo, llamar size()a la lista incorrecta).


Me alegro de que te haya gustado. Revisé mi entrada para admitir la eliminación de todas las instancias de "a", no solo la primera. :-)
Chris Jester-Young

Ooff ... derribado en la línea de meta. Sabía que debería haber seguido editando. Este sistema requiere algo de tiempo para acostumbrarse. ¡Buena edición!
Dustman

GHad: ¿Has leído mi Edit2 arriba? Aborda exactamente lo que mencionaste y se publicó antes de tu publicación.
Chris Jester-Young

2
¿Por qué no list.toArray (new String [list.size ()]) en lugar de new String [0], ya que el código usará la nueva matriz si tiene el tamaño correcto?
cynicalman

Sí, eso funciona. De lo contrario, algún código (en la biblioteca de clases de Java, principalmente) almacena una instancia estática de String [0] (y otras matrices similares de tamaño cero) y pasa las instancias estáticas en lugar de una nueva cada vez. :-)
Chris Jester-Young

31

Una alternativa en Java 8:

String[] filteredArray = Arrays.stream(array)
    .filter(e -> !e.equals(foo)).toArray(String[]::new);

Esta debería ser la respuesta aceptada. Aunque no es agradable, considerando que otros lenguajes de programación pueden hacer esto con menos código y más claro ... esto es lo mejor que Java tiene para ofrecer sin depender de otra biblioteca.
Jason

7
Aunque ordenado, esto será sorprendentemente ineficiente en comparación con System.arraycopy. Probablemente no debería hacer esto en código real.
Bill K

si foo es una variable de cadena dinámica, está arrojando un error de tiempo de compilación La variable local foo definida en un ámbito adjunto debe ser final o efectivamente final
Mahender Reddy Yasa

La pregunta dice "Dada una matriz de n objetos" , por Stream.of(foo).filter(s -> ! s.equals("a")).toArray()lo que sería suficiente.
Kaplan

20

Haga un Listfuera de la matriz con Arrays.asList()y llame remove()a todos los elementos apropiados. Luego, llame toArray()a la 'Lista' para volver a formar una matriz.

No es muy eficaz, pero si lo encapsula correctamente, siempre puede hacer algo más rápido más adelante.


3
Re tu comentario: Está bien, te acostumbrarás pronto. :-) Publiqué mi publicación porque no quería que los lectores tuvieran la idea de que se pueden eliminar elementos del resultado de Arrays.asList () (es una lista inmutable), así que pensé que un ejemplo podría solucionarlo. :-)
Chris Jester-Young

Uh, me refiero a la lista no redimensionable (agregar () y eliminar () no funcionan). :-P Todavía tiene un método set () utilizable. :-)
Chris Jester-Young

Aunque pueda parecer extraño, mi experiencia es que la penalización de rendimiento de este enfoque es mínima.
Marcus Downing

8
¿Qué pasa con esto? @Chris señaló que la lista resultante de Arrays.asList()no es compatible remove(). Entonces, ¿esta respuesta es completamente inválida? Parece que tal vez se eliminaron algunos comentarios, así que no sé si esto se discutió.
LarsH

1
Tanto Chris como LarsH tienen razón: las listas respaldadas por matrices (en otras palabras, las que se crean con Arrays.asList ()) son estructuralmente inmutables, lo que invalida totalmente esta respuesta. Sin embargo, a partir de ahora, veo diecisiete votos a favor. Hace que uno se pregunte ...
Igor Soudakevitch

15

Siempre puedes hacer:

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);

6

Puede utilizar una biblioteca externa:

org.apache.commons.lang.ArrayUtils.remove(java.lang.Object[] array, int index)

Está en el proyecto Apache Commons Lang http://commons.apache.org/lang/


ArrayUtils.removeElement(boolean[] array, boolean element)también es muy útil.
Scai

5

Ver código a continuación

ArrayList<String> a = new ArrayList<>(Arrays.asList(strings));
a.remove(i);
strings = new String[a.size()];
a.toArray(strings);

4

Si necesita eliminar varios elementos de la matriz sin convertirlos Listni crear una matriz adicional, puede hacerlo en O (n) sin depender del recuento de elementos para eliminar.

Aquí, aes la matriz inicial, int... rhay distintos índices ordenados (posiciones) de elementos para eliminar:

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"
}  

Pequeñas pruebas:

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]

En su tarea, primero puede escanear la matriz para recopilar las posiciones de "a", luego llamar removeItems().


1
Por favor, no hagas esto. Es confuso, lento y propenso a errores. Simplemente use System.arraycopy () en su lugar, aunque tiene puntos de bonificación por el hecho de que si va a manipular una matriz de esta manera, debe rastrear la longitud.
Bill K

3

Algo sobre hacer una lista, luego eliminarlo y luego volver a una matriz me parece incorrecto. No lo he probado, pero creo que lo siguiente funcionará mejor. Sí, probablemente estoy pre-optimizando indebidamente.

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];
   }
}

3

Me doy cuenta de que esta es una publicación muy antigua, pero algunas de las respuestas aquí me ayudaron, ¡así que aquí está mi tuppence 'un penique!

Luché para que esto funcionara durante bastante tiempo antes de cambiar el tamaño de la matriz en la que estoy escribiendo de nuevo, a menos que los cambios realizados en ArrayListla lista dejen el tamaño de la lista sin cambios.

Si el ArrayListque está modificando termina con más o menos elementos de los que comenzó, la línea List.toArray()causará una excepción, por lo que necesita algo como List.toArray(new String[] {})o List.toArray(new String[0])para crear una matriz con el nuevo tamaño (correcto).

Suena obvio ahora que lo sé. No es tan obvio para un novato de Android / Java que se está familiarizando con construcciones de código nuevas y desconocidas y no es obvio en algunas de las publicaciones anteriores aquí, así que solo quería dejar este punto muy claro para cualquier otra persona que se rasca la cabeza durante horas como yo. !


Sentí la necesidad de publicar esto, ya que a menudo uso fragmentos de código que no funcionan porque me perdí algo que otros programadores dan por sentado. GHad hizo el punto sobre el tamaño de la matriz que hizo que mi código funcionara (gracias por dejarlo en claro). Probar cosas es la forma de aprender, y si eso significa que merezco todo lo que obtengo por tomar código de SO y tratar de entender cómo / por qué funciona, entonces que así sea. Como aficionado no remunerado, ¡no soy el genio de Java que a algunos les gusta pensar que son! Afortunadamente, la mayoría de los colaboradores de SO responden preguntas para ayudar a otros a escribir un mejor código: ¡Por eso, se merece un agradecimiento!
DDSports

3

Hay muchas respuestas aquí; el problema, tal como lo veo, es que no dijo POR QUÉ está usando una matriz en lugar de una colección, así que permítame sugerir un par de razones y qué soluciones se aplicarían (la mayoría de las soluciones ya han sido respondidas en otras preguntas aquí, por lo que no entraré en demasiados detalles):

motivo: no sabía que existía el paquete de colección o no confiaba en él

solución: Utilice una colección.

Si planea agregar / eliminar desde el medio, use una LinkedList. Si está realmente preocupado por el tamaño o, a menudo, indexa directamente en el medio de la colección, use una ArrayList. Ambos deberían tener operaciones de eliminación.

motivo: le preocupa el tamaño o desea controlar la asignación de memoria

solución: use una ArrayList con un tamaño inicial específico.

Una ArrayList es simplemente una matriz que se puede expandir, pero no siempre es necesario que lo haga. Será muy inteligente agregar / eliminar elementos, pero nuevamente si está insertando / eliminando MUCHO del medio, use una LinkedList.

motivo: tiene una matriz entrando y una matriz saliendo, por lo que desea operar en una matriz

solución: conviértalo en un ArrayList, elimine el elemento y conviértalo de nuevo

motivo: cree que puede escribir un código mejor si lo hace usted mismo

solución: no puede, use una matriz o una lista vinculada.

motivo: esta es una tarea de clase y no está permitido o no tiene acceso a las apis de la colección por algún motivo

suposición: necesita que la nueva matriz tenga el "tamaño" correcto

solución: escanee la matriz en busca de elementos coincidentes y cuéntelos. Cree una nueva matriz del tamaño correcto (tamaño original - número de coincidencias). use System.arraycopy repetidamente para copiar cada grupo de elementos que desea retener en su nueva matriz. Si esta es una asignación de clase y no puede usar System.arraycopy, simplemente cópielos uno a la vez a mano en un bucle, pero nunca lo haga en el código de producción porque es mucho más lento. (Estas soluciones se detallan en otras respuestas)

motivo: necesitas ejecutar bare metal

suposición: no DEBE asignar espacio innecesariamente o tomar demasiado tiempo

suposición: está rastreando el tamaño utilizado en la matriz (longitud) por separado porque, de lo contrario, tendría que reasignar su matriz para eliminaciones / inserciones.

Un ejemplo de por qué es posible que desee hacer esto: una sola matriz de primitivas (digamos valores int) está tomando una parte significativa de su RAM, ¡como el 50%! Un ArrayList los forzaría en una lista de punteros a objetos Integer que usarían unas pocas veces esa cantidad de memoria.

solución: Itere sobre su matriz y cada vez que encuentre un elemento para eliminar (llamémoslo elemento n), use System.arraycopy para copiar la cola de la matriz sobre el elemento "eliminado" (la fuente y el destino son la misma matriz): es lo suficientemente inteligente como para hacer la copia en la dirección correcta para que la memoria no se sobrescriba a sí misma:

 System.arraycopy (ary, n + 1, ary, n, longitud-n) 
 longitud--;

Probablemente querrá ser más inteligente que esto si está eliminando más de un elemento a la vez. Solo movería el área entre una "coincidencia" y la siguiente en lugar de toda la cola y, como siempre, evite mover cualquier trozo dos veces.

En este último caso, debe hacer el trabajo usted mismo, y usar System.arraycopy es realmente la única forma de hacerlo, ya que elegirá la mejor forma posible de mover la memoria para la arquitectura de su computadora; debería ser muchas veces más rápido que cualquier código que pueda escribir usted mismo.


2

Matriz inicial

   int[] array = {5,6,51,4,3,2};

si desea eliminar 51 que es el índice 2, use lo siguiente

 for(int i = 2; i < array.length -1; i++){
    array[i] = array[i + 1];
  }

1

EDITAR:

Se borró el punto con los nulos en la matriz. Perdón por mis comentarios.

Original:

Ehm ... la línea

array = list.toArray(array);

reemplaza todos los espacios en la matriz donde el elemento eliminado ha estado con nulo . Esto puede ser peligroso , porque los elementos se eliminan, ¡pero la longitud de la matriz sigue siendo la misma!

Si desea evitar esto, use una nueva matriz como parámetro para toArray (). Si no desea utilizar removeAll, un conjunto sería una alternativa:

        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));

Da:

[a, bc, dc, a, ef]
[dc, ef, bc]

Donde, como la respuesta aceptada actual de Chris Yester Young produce:

[a, bc, dc, a, ef]
[bc, dc, ef, null, ef]

con el codigo

    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));

sin ningún valor nulo dejado atrás.


Buen intento, pero no puro. Publiqué una edición sobre este tema exactamente, mucho antes de que hicieras tu publicación. Entonces, aunque estás "técnicamente correcto", no aprecio que intentes que la gente desplace mi publicación. Solo pensé que deberías saber eso.
Chris Jester-Young

No se trata de un desplazamiento posterior, sino de evitar errores y códigos peligrosos. Greetz GHad
GHad

Los errores se pueden evitar si la gente lee toda mi publicación (incluidos los dos apéndices). Si las personas simplemente cortan y pegan código sin pensar, se merecen todo lo que obtienen. A los programadores se les paga por lo que hacen porque ejercitan su cerebro ... eso espero. [continúa]
Chris Jester-Young

1
[continuación] Su código también es "peligroso" si la gente no es consciente del hecho de que al usar un hash, los elementos se vuelven desordenados. Por supuesto, los programadores pensantes se dan cuenta de esto, pero si llamas a mi código peligroso porque la gente corta y pega sin pensar, es justo decir lo mismo del tuyo.
Chris Jester-Young

Seguro, tienes razón sobre el hash. Y como el punto está claro en su segunda edición, debo haber leído esto en exceso. Como se dijo, solo quería evitar una matriz con valores nulos y duplicación. Puede cambiar su código según su segunda edición y dejar un comentario en Edit3 sobre la matriz. No quería
atacarte

1

Mi pequeña contribución a este problema.

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;
}

}


Esta es una respuesta realmente buena, aunque su excelente código de prueba hace que parezca mucho más grande de lo que realmente es. La solución completa podría ser una copia de matriz de una sola línea (suponiendo que use la misma matriz, que es lo que le gustaría hacer si estuviera trabajando tan cerca del metal que estuviera usando matrices en lugar de colecciones).
Bill K

gracias, lo escribí para que los lectores puedan ver un poco lo que sucede bajo el capó y probarlo, ahora lo miro de nuevo System.arraycopy (tmp1, 0, tmp2, 0, tmp2.length); se puede eliminar y reemplazar por algo como for (i = 0; i <(longitud de arr.-t); i ++) {tmp2 [i] = tmp1 [i]; } también puede crear un búfer de bytes y copiar 8 bytes a la vez en una máquina de 64 bits para obtener un rendimiento de copia adicional
Andre

0

¿Depende de lo que quieras decir con "eliminar"? Una matriz es una construcción de tamaño fijo; no puede cambiar la cantidad de elementos que contiene. Por lo tanto, puede a) crear una nueva matriz más corta sin los elementos que no desea ob) asignar las entradas que no desea a algo que indique su estado "vacío"; generalmente es nulo si no está trabajando con primitivas.

En el primer caso, cree una lista de la matriz, elimine los elementos y cree una nueva matriz de la lista. Si el rendimiento es importante, repita la matriz asignando los elementos que no deben eliminarse a una lista y luego cree una nueva matriz a partir de la lista. En el segundo caso, simplemente revise y asigne un valor nulo a las entradas de la matriz.


0

Arrgh, no puedo hacer que el código aparezca correctamente. Lo siento, lo tengo funcionando. Lo siento de nuevo, no creo haber leído bien la pregunta.

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++;
    }
}

0

Copiará todos los elementos excepto el que tiene el índice 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) );
            }

0

En una matriz de cadenas como

String name = 'abcdeafbde' // podría ser como String name = 'aa bb cde aa f bb de'

Construyo la siguiente clase

class clearname{
def parts
def tv
public def str = ''
String name
clearname(String name){
    this.name = name
    this.parts = this.name.split(" ")
    this.tv = this.parts.size()
}
public String cleared(){

        int i
        int k
        int j=0        
    for(i=0;i<tv;i++){
        for(k=0;k<tv;k++){
            if(this.parts[k] == this.parts[i] && k!=i){
               this.parts[k] = '';
                j++
            }
        }
    }
    def str = ''
    for(i=0;i<tv;i++){
        if(this.parts[i]!='')

           this.str += this.parts[i].trim()+' '
    } 
    return this.str    
}}



return new clearname(name).cleared()

obteniendo este resultado

a B C D e F

Espero que este código ayude a todos.


0

Si no importa el orden de los elementos. puede intercambiar entre los elementos foo [x] y foo [0], luego llamar a foo.drop (1).

foo.drop(n) elimina (n) primeros elementos de la matriz.

Supongo que esta es la forma más sencilla y eficiente de hacerlo.

PD : indexOfse puede implementar de muchas formas, esta es mi versión.

Integer indexOf(String[] arr, String value){
    for(Integer i = 0 ; i < arr.length; i++ )
        if(arr[i] == value)
            return i;         // return the index of the element
    return -1                 // otherwise -1
}

while (true) {
   Integer i;
   i = indexOf(foo,"a")
   if (i == -1) break;
   foo[i] = foo[0];           // preserve foo[0]
   foo.drop(1);
}

-3

Utilizar:

list.removeAll(...);
//post what char you need in the ... section

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.