如何从Java中的数组中删除对象?


82

给定一个n个对象的数组,假设它是一个string数组,它具有以下值:

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

如何删除/删除数组中所有等于“ a”的字符串/对象?


2
您无法在Java中调整数组的大小。我假设您不想只将元素设置为空,因为那将是微不足道的。是否要转移其他元素以消除差距?
丹·代尔

1
现在我知道我可以做到,这是微不足道的。;) 谢谢!
ramayac

Answers:


112

[如果需要一些现成的代码,请滚动到我的“ Edit3”(剪切后)。其余的供后代使用。]

为了充实Dustman的想法

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

编辑:我现在使用Arrays.asList而不是Collections.singleton:单例仅限于一个条目,而该asList方法允许您添加其他字符串以供以后过滤:Arrays.asList("a", "b", "c")

Edit2:以上方法保留了相同的数组(因此数组的长度仍然相同);最后一个元素之后的元素设置为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每次调用函数时对其进行编辑。

愤世嫉俗的人的建议(请参阅评论)也将有助于解决乱扔垃圾的问题,为公平起见,我应该提一下:

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

我更喜欢我的方法,因为更容易弄错显式大小(例如,调用size()错误的列表)。


很高兴您喜欢。我修改了条目以支持删除所有“ a”实例,而不仅仅是第一个实例。:-)
克里斯·杰斯特·杨

Ooff ...在终点线被击落。知道我应该继续编辑。这个系统需要一些习惯。好编辑!
垃圾工

加德:您读过我的Edit2吗?它完全符合您提到的内容,并且已在您发布之前发布。
克里斯·杰斯特·杨

2
为什么不使用list.toArray(new String [list.size()])而不是new String [0],因为如果大小正确,代码将使用新数组?
cynicalman's

是的,那行得通。否则,某些代码(主要在Java类库中)将存储String [0]的静态实例(以及其他类似的零大小数组),并传递静态实例,而不是每次都更新一个。:-)
克里斯·杰斯特·杨

31

Java 8中的替代方法:

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

这应该是公认的答案。尽管不太考虑其他编程语言可以用更少和更清晰的代码来完成此操作……但这是Java最好的选择,而无需依赖其他库。
杰森

7
尽管很简洁,但是与System.arraycopy相比,它的效率非常低。可能不应该在真实代码中执行此操作。
Bill K

如果foo是动态字符串变量,则会引发编译时错误在封闭范围内定义的局部变量foo必须是最终的或有效的最终
Mahender Reddy Yasa,

该问题指出“给出n个对象的数组” -这样Stream.of(foo).filter(s -> ! s.equals("a")).toArray()就足够了。
卡普兰

20

使用List进行数组的Arrays.asList()调用,并调用remove()所有适当的元素。然后调用toArray()“列表”以再次返回数组。

性能不是很出色,但是如果正确封装它,以后总是可以更快地执行。


3
发表您的评论:没关系,您很快就会习惯它。:-)我之所以发表我的文章,是因为我不希望读者想到可以从Arrays.asList()的结果中删除元素(这是一个不可变的列表)的想法,所以我认为一个例子可以解决这个问题。:-)
克里斯·杰斯特·杨

嗯,我的意思是列表无法调整大小(add()和remove()不起作用)。:-P它仍然具有可用的set()方法。:-)
克里斯·杰斯特·杨

尽管可能看起来很奇怪,但我的经验是这种方法的性能损失很小。
Marcus Downing

8
怎么了 @Chris指出产生的清单Arrays.asList()不支持remove()。那么这个答案完全无效吗?看来有些评论已被删除,所以我不知道是否在讨论这个问题。
LarsH 2012年

1
Chris和LarsH都是正确的:支持数组的列表(换句话说,使用Arrays.asList()创建的列表)在结构上是不可变的,这完全使该答案无效。但是,到目前为止,我看到了十七个投票。令人惊讶...
Igor Soudakevitch

15

您可以随时这样做:

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

您可以使用外部库:

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

它在项目Apache Commons Lang中http://commons.apache.org/lang/


ArrayUtils.removeElement(boolean[] array, boolean element)也非常有用。
scai

5

见下面的代码

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

4

如果需要从数组中删除多个元素而不将其转换为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()


1
请不要这样做。这令人困惑,缓慢且容易出错。只需使用System.arraycopy()即可-尽管由于要以这种方式操作数组必须跟踪长度,这一点还是有加分的。
比尔K

3

关于将其制成列表然后删除然后返回到数组的事情使我感到错误。尚未测试,但我认为以下方法会更好。是的,我可能未适当地进行预优化。

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

我意识到这是一个非常古老的帖子,但是这里的一些答案帮助了我,所以这就是我的惊喜'哈彭妮的价值!

在努力重新调整要写入的数组的大小之前,我很难使它工作好一会儿,除非ArrayList对列表大小保持不变,否则必须重新调整大小。

如果ArrayList您要修改的最终比开始时包含更多或更少的元素,则该行将List.toArray()导致异常,因此您需要类似List.toArray(new String[] {})List.toArray(new String[0])以便创建具有新(正确)大小的数组。

我知道这听起来很明显。对于正在使用新的和不熟悉的代码构造的Android / Java新手来说,不是很明显,在这里的一些早期文章中也不是很明显,所以只想让其他人像我一样费时费力地阐明这一点!


我觉得有必要发布此代码,因为我经常使用不起作用的代码片段,因为我错过了其他程序员认为理所当然的东西。GHad指出了有关数组大小的要点,这可以使我的代码正常工作(感谢您澄清这一点)。尝试一些东西是学习的方式,如果这意味着我应该得到从SO那里获取代码并尝试了解其工作原理/原因,那么我应该得到的一切。作为一名无偿的业余爱好者,我不是某些人想像的Java天才!值得庆幸的是,大多数SO贡献者回答了问题,以帮助其他人编写更好的代码:为此,您值得感谢!
DDSports 2013年

3

这里有很多答案-我看到的问题是你没有说为什么要使用数组而不是集合,所以让我提出一些原因以及哪些解决方案适用(大多数解决方案在这里已经在其他问题中得到了解答,因此我不会赘述):

原因:您不知道收集程序包是否存在或不信任它

解决方案:使用集合。

如果您打算从中间添加/删除,请使用LinkedList。如果您真的担心大小或经常索引到集合的中间,请使用ArrayList。这两个都应具有删除操作。

原因:您担心大小或想要控制内存分配

解决方案:使用具有特定初始大小的ArrayList。

ArrayList只是可以自我扩展的数组,但并不总是需要这样做。添加/删除项目非常聪明,但是如果要从中间插入/删除LOT,请再次使用LinkedList。

原因:您有一个数组进来,一个数组出去了-因此您想对数组进行操作

解决方案:将其转换为ArrayList,删除该项并将其转换回

原因:您认为自己编写可以编写更好的代码

解决方案:不能使用数组或链接列表。

原因:这是一个类分配,由于某种原因,您不允许使用或无法访问集合api

假设:您需要新数组为正确的“大小”

解决方案:扫描数组以查找匹配项并计数。创建一个具有正确大小(原始大小-匹配数)的新数组。重复使用System.arraycopy将希望保留的每组项目复制到新Array中。如果这是一个类分配,并且您不能使用System.arraycopy,则只需一次循环手动将它们复制一次,但不要在生产代码中这样做,因为它要慢得多。(这些解决方案在其他答案中都有详细说明)

原因:您需要运行裸机

假设:您不得不必要地分配空间或花费太长时间

假设:您正在分别跟踪数组中使用的大小(长度),因为否则,您将不得不为删除/插入重新分配数组。

一个为什么您可能想要这样做的例子:一个原始数组(假设是int值)占用了您的ram的很大一部分,例如50%!ArrayList将强制这些指针进入指向Integer对象的指针列表,该指针将使用该数量的内存。

解决方案:遍历数组,每当找到要删除的元素时(将其称为元素n),请使用System.arraycopy将数组的尾部复制到“已删除”的元素上(源和目标是同一数组)-足够聪明,可以按正确的方向进行复制,因此内存不会覆盖自身:

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

如果一次要删除多个元素,您可能会想比这更聪明。您只会在一个“匹配”和下一个“匹配”之间移动区域,而不是整个尾巴,并且一如既往,避免将任何块移动两次。

在这最后一种情况下,您绝对必须自己完成工作,而使用System.arraycopy实际上是唯一的方法,因为它将为您的计算机体系结构选择移动内存的最佳方法-它的速度应该快很多倍比您可以合理编写自己的任何代码。


2

初始数组

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

如果要删除索引2的51,请使用以下命令

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

1

编辑:

数组中带有空值的点已被清除。对不起,我的评论。

原版的:

嗯...线

array = list.toArray(array);

null替换数组中所有已删除元素的间隙。这可能很危险,因为删除了元素,但是数组的长度保持不变!

如果要避免这种情况,请使用新的Array作为toArray()的参数。如果您不想使用removeAll,则可以选择Set:

        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]

克里斯·耶斯特·杨(Chris Yester Young)当前接受的答案如下:

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

没有留下任何空值。


不错的尝试,但没有雪茄。我在发布您的帖子之前就对该主题进行了编辑。因此,尽管您“在技术上是正确的”,但我不感谢您尝试让人们取代我的职位。我只是以为你应该知道这一点。
克里斯·杰斯特·杨

这与后置位移无关,而与避免错误和危险代码有关。格蕾兹·加德
加德(Gad)

如果人们阅读了我的整个帖子(包括两个附录),就可以避免错误。如果人们只是不加思索地剪切和粘贴代码,那么他们应该得到的一切。程序员会因为他们的大脑锻炼而得到报酬……我希望。[续]
克里斯·杰斯特·杨

1
[续]如果人们不认识到使用散列来使项目变得混乱的事实,那么您的代码也是“危险的”。当然,有思想的程序员会意识到这一点,但是如果您将我的代码称为危险的,因为人们在不加思索的情况下进行剪切和粘贴,那么对您说同样的话是很公平的。
克里斯·杰斯特·杨

当然,您对哈希是正确的。就像您第二次编辑所指出的那样,我一定已经读懂了。如前所述,只是想避免具有空值和重复项的数组。您可以根据第二次编辑更改代码,并在Edit3中对数组发表评论。不想攻击您
GHad

1

我对这个问题的贡献不大。

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

}


尽管您出色的测试代码使它看起来比实际要大得多,但这确实是一个很好的答案。整个解决方案可以是单行阵列复制(假设您使用的阵列与如果您使用的金属非常接近要使用阵列而不是集合的金属相同,则需要这样做)。
比尔K

谢谢,我写了它,以便读者可以看一些底层的内容并对其进行测试,现在我再次对其进行查看System.arraycopy(tmp1,0,tmp2,0,tmp2.length); 可以删除并替换为for(i = 0; i <(arr.length-t); i ++){tmp2 [i] = tmp1 [i]; }您还可以创建一个字节缓冲区,并在一台64位计算机上一次复制8个字节,以提高复制性能
Andre

0

这取决于您所说的“删除”是什么意思?数组是固定大小的构造-您不能更改其中的元素数量。因此,您可以a)创建一个新的,较短的数组,其中不包含不需要的元素;或者b)将不需要的条目分配给表明其“空”状态的东西;如果您不使用基元,则通常为null。

在第一种情况下,从数组创建一个列表,删除元素,然后从列表创建一个新的数组。如果性能很重要,请在数组上进行迭代,将不应删除的所有元素分配到列表中,然后从列表中创建一个新数组。在第二种情况下,只需遍历并将null分配给数组条目。


0

抱歉,我无法正确显示代码。抱歉,我知道了。再次抱歉,我认为我没有正确阅读问题。

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

将复制除索引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

在像这样的字符串数组中

字符串名称='abcdeafbde'//可能像字符串名称='aa bb cde aa f bb de'

我建立以下课程

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

得到这个结果

abcdef

希望这段代码对任何人有帮助


0

如果没关系,元素的顺序。您可以在元素foo [x]和foo [0]之间交换,然后调用foo.drop(1)。

foo.drop(n) 从数组中删除(n)个第一元素。

我想这是最简单且最节省资源的方法。

PSindexOf可以通过多种方式实现,这是我的版本。

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

使用:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.