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

How do I remove objects from an array in Java?

提问人:ramayac 提问时间:9/22/2008 最后编辑:Peter Mortensenramayac 更新时间:2/24/2021 访问量:384991

问:

给定一个包含 n 个对象的数组,假设它是一个字符串数组,它具有以下值:

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

我必须做什么才能删除/移除数组中所有等于“a”的字符串/对象?

Java 数组 结构 数据操作

评论

2赞 Dan Dyer 9/22/2008
您无法在 Java 中调整数组的大小。我假设您不想只是将元素为空,因为这将是微不足道的。是否要移动其他元素以消除间隙?
1赞 ramayac 9/22/2008
这是微不足道的,现在我知道我能做到。;)谢谢!

答:

21赞 Dustman 9/22/2008 #1

用 做一个数组,并调用所有适当的元素。然后调用“列表”以再次重新制作成数组。ListArrays.asList()remove()toArray()

性能不是很好,但如果你正确地封装它,你以后总是可以更快地做一些事情。

评论

3赞 C. K. Young 9/22/2008
回复您的评论:没关系,你很快就会习惯的。:-)我发布我的帖子是因为我不想让读者认为可以从 Arrays.asList() 的结果中删除元素(它是一个不可变的列表),所以我认为一个例子可以解决这个问题。:-)
0赞 C. K. Young 9/22/2008
呃,我的意思是不可调整大小的列表(add() 和 remove() 不起作用)。:-P它仍然有一个可用的 set() 方法。:-)
0赞 Marcus Downing 9/22/2008
虽然这看起来很奇怪,但我的经验是,这种方法对性能的损失是最小的。
8赞 LarsH 3/22/2012
这是怎么回事?@Chris指出,生成的列表不支持 .那么这个答案是完全无效的吗?看起来可能有些评论被删除了,所以我不知道是否讨论过这个问题。Arrays.asList()remove()
1赞 Igor Soudakevitch 4/22/2016
Chris 和 LarsH 都是对的:数组支持的列表(换句话说,那些使用 Arrays.asList() 创建的列表)在结构上是不可变的,这完全使这个答案无效。然而,截至目前,我看到 17 个赞成票。让人不禁好奇......
-7赞 alfinoba 9/22/2008 #2

将 null 分配给数组位置。

115赞 C. K. Young 9/22/2008 #3

[如果你想要一些现成的代码,请滚动到我的“Edit3”(剪切后)。其余的都在这里供后代使用。

为了充实 Dustman 的想法

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

编辑:我现在使用代替:singleton仅限于一个条目,而该方法允许您添加其他字符串以稍后过滤掉:。Arrays.asListCollections.singletonasListArrays.asList("a", "b", "c")

Edit2:上述方法保留了相同的数组(因此数组的长度仍然相同);最后一个后面的元素设置为 null。如果想要一个大小完全符合要求的新数组,请改用以下方法:

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

编辑 3:如果您在同一类中频繁使用此代码,您可能希望考虑将其添加到您的类中:

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

这样一来,就不会再在堆堆里乱扔无用的空字符串数组,否则每次调用函数时都会被 ed。new

愤世嫉俗者的建议(见评论)也将有助于解决乱扔垃圾的问题,为了公平起见,我应该提到它:

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

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

评论

0赞 C. K. Young 9/22/2008
很高兴你喜欢。我修改了我的条目以支持删除所有“a”实例,而不仅仅是第一个实例。:-)
0赞 Dustman 9/22/2008
哎呀......在终点线被击落。知道我应该继续编辑。这个系统需要一些时间来适应。好编辑!
0赞 C. K. Young 9/23/2008
GHad:你读过我上面的 Edit2 吗?它准确地解决了您提到的内容,并且是在您的帖子之前发布的。
2赞 cynicalman 9/25/2008
为什么不是 list.toArray(new String[list.size()]) 而不是 new String[0],因为如果新数组的大小正确,代码将使用它?
0赞 C. K. Young 9/27/2008
是的,这行得通。否则,某些代码(主要在 Java 类库中)存储 String[0] 的静态实例(以及其他类似的零大小数组),并传递静态实例,而不是每次都新建一个。:-)
3赞 shsteimer 9/22/2008 #4

关于列出它然后删除然后回到数组的某些东西让我感到错误。尚未测试,但我认为以下内容会表现得更好。是的,我可能过度预优化了。

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];
   }
}
0赞 AngelOfCake 9/22/2008 #5

哎呀,我无法让代码正确显示。对不起,我让它工作了。再次抱歉,我认为我没有正确阅读这个问题。

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++;
    }
}
1赞 GHad 9/23/2008 #6

编辑:

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

源语言:

嗯......线

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

不留下任何 null 值。

评论

0赞 C. K. Young 9/23/2008
不错的尝试,但没有雪茄。在你发帖之前,我就这个主题发布了一个编辑。所以,虽然你“技术上是正确的”,但我不欣赏你试图让人们取代我的帖子。我只是觉得你应该知道这一点。
0赞 GHad 9/23/2008
这不是关于后位移,而是关于避免错误和危险的代码。Greetz GHad
0赞 C. K. Young 9/24/2008
如果人们阅读了我的整篇文章(包括两个附录),则可以避免错误。如果人们只是不假思索地剪切和粘贴代码,那么他们应该得到他们所得到的一切。程序员通过他们的工作获得报酬,因为他们锻炼了自己的大脑......我希望。[继续]
1赞 C. K. Young 9/24/2008
[续]如果人们没有意识到通过使用哈希,项目会变得无序,那么你的代码也是“危险的”。当然,有思想的程序员也意识到了这一点,但如果你说我的代码很危险,因为人们不假思索地剪切和粘贴,那么说你的代码也是公平的。
0赞 GHad 9/25/2008
当然,你对哈希的看法是对的。由于您的第二次编辑清楚地表明了这一点,我一定过度阅读了这一点。如前所述,只是想避免具有 null 值和重复的数组。您可以根据第二次编辑更改代码,并在 Edit3 中对数组发表评论。不想攻击你
16赞 anon 9/23/2008 #7

你总是可以做到的:

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);
0赞 DJClayworth 9/25/2008 #8

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

在第一种情况下,从数组创建一个 List,删除元素,然后从列表中创建一个新数组。如果性能很重要,请循环访问数组,将不应删除的任何元素分配到列表中,然后从列表中创建一个新数组。在第二种情况下,只需遍历并将 null 分配给数组条目。

7赞 bugs_ 2/4/2011 #9

您可以使用外部库:

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

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

评论

0赞 scai 12/17/2018
ArrayUtils.removeElement(boolean[] array, boolean element)也非常有用。
3赞 DDSports 7/5/2013 #10

我意识到这是一个非常古老的帖子,但这里的一些答案帮助了我,所以这是我的 tuppence' ha'penny's worth!

在纠结我写回的数组需要调整大小之前,我努力让它工作了很长一段时间,除非对列表大小保持不变所做的更改。ArrayList

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

现在我知道了,这听起来很明显。对于一个正在掌握新的和不熟悉的代码结构的 Android/Java 新手来说,这并不那么明显,而且从这里的一些早期帖子中也不明显,所以只是想让其他任何人真正清楚地了解这一点像我一样挠头几个小时!

评论

0赞 DDSports 7/5/2013
我觉得有必要发布这个,因为我经常使用不起作用的代码片段,因为我错过了其他编码人员认为理所当然的东西。GHad 提出了关于数组大小的观点,这让我的代码正常工作(感谢您明确说明这一点)。尝试东西是学习的方式,如果这意味着我应该从 SO 获取代码并试图理解它如何/为什么工作,那就这样吧。作为一个没有报酬的业余爱好者,我不是一些人喜欢认为的 Java 天才!值得庆幸的是,大多数 SO 贡献者都会回答问题以帮助其他人编写更好的代码:为此,你值得感谢!
1赞 Andre 11/30/2013 #11

我对这个问题的贡献很小。

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

}

评论

0赞 Bill K 5/25/2017
这是一个非常好的答案,尽管您出色的测试代码使它看起来比实际大得多。整个解决方案可以是单行数组复制(假设你使用相同的数组,如果你在金属附近工作,你使用数组而不是集合,这就是你想要做的事情)。
0赞 Andre 6/5/2017
谢谢,我写它是为了让读者可以看到引擎盖下发生的事情并对其进行测试,现在我再看一遍 System.arraycopy(tmp1, 0, tmp2, 0, tmp2.length) ;可以删除并替换为 for(i = 0; i < (arr.length - t); i++){ tmp2[i] = tmp1[i]; } 您还可以创建一个字节缓冲区并在 64 位机器上一次复制 8 个字节,以获得额外的复制性能
6赞 Ali 1/27/2014 #12

请参阅下面的代码

ArrayList<String> a = new ArrayList<>(Arrays.asList(strings));
a.remove(i);
strings = new String[a.size()];
a.toArray(strings);
33赞 Vitalii Fedorenko 4/21/2014 #13

Java 8 中的另一种选择:

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

评论

0赞 Jason 3/6/2016
这应该是公认的答案。虽然考虑到其他编程语言可以用更少、更清晰的代码来做到这一点,但这并不漂亮......这是 Java 在不依赖其他库的情况下所能提供的最好的。
8赞 Bill K 5/25/2017
虽然很整洁,但与 System.arraycopy 相比,这将是非常低效的。可能不应该在实际代码中这样做。
0赞 Mahender Reddy Yasa 8/2/2018
如果 foo 是动态字符串变量,它会抛出编译时错误 在封闭作用域中定义的局部变量 foo 必须是最终的或实际上是最终的
0赞 Kaplan 4/16/2020
该问题指出“给定一个 n 个对象的数组”——这样就足够了。Stream.of(foo).filter(s -> ! s.equals("a")).toArray()
0赞 Kaplan 2/24/2021
更多时候,我只想删除几个相等条目中的第一个
5赞 Alex Salauyou 5/10/2015 #14

如果您需要从数组中删除多个元素而不将其转换为其他数组,则可以在 O(n) 中执行此操作,而不依赖于要删除的项目数。List

这里,是初始数组,是要删除的元素的不同有序索引(位置):aint... 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赞 Bill K 5/25/2017
请不要这样做。它令人困惑、缓慢且容易出错。只需改用 System.arraycopy() 即可 - 尽管如果您要以这种方式操作数组,则必须跟踪长度这一事实的奖励点。
0赞 PauLy 6/12/2016 #15

将复制除索引为 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) );
            }
4赞 Bill K 5/25/2017 #16

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

原因:您不知道收集包的存在或不信任它

解决方案:使用集合。

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

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

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

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

reason:你有一个数组进入,一个数组输出 -- 所以你想对一个数组进行操作

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

reason:你认为自己做可以写出更好的代码

解决方案:你不能,使用数组或链表。

原因:这是一项类作业,不允许您访问,或者由于某种原因您无权访问集合 API

assumption:您需要新数组的“大小”正确

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

原因:您需要运行裸机

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

假设:您正在单独跟踪数组中使用的大小(长度),否则您必须重新分配数组以进行删除/插入。

举个例子来说明你为什么可能想要这样做:一个基元数组(比如说 int 值)占用了你的 ram 的很大一部分——比如 50%!ArrayList 会强制这些对象进入指向 Integer 对象的指针列表,这些对象将使用几倍于该内存量的内存。

解决方案:遍历你的数组,每当你找到一个要删除的元素(我们称之为元素 n)时,使用 System.arraycopy 将数组的尾部复制到“已删除”元素上(源和目标是同一个数组)——它足够聪明,可以在正确的方向上进行复制,这样内存就不会覆盖自己:

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

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

在最后一种情况下,你绝对必须自己做这项工作,而使用 System.arraycopy 确实是唯一的方法,因为它将选择最好的方式来移动你的计算机体系结构的内存——它应该比你自己合理编写的任何代码快很多倍。

2赞 Ebin Joy 11/20/2018 #17

初始数组

   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赞 Orlando Reyes 6/18/2019 #18

在字符串数组中,例如

String name = 'a b c d e a f b d e' // 可以像 String name = 'aa bb c d e aa f bb d e'

我构建了以下类

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

得到这个结果

a、b、c、d、e、f

希望此代码对任何人有所帮助 问候

0赞 milevyo 8/7/2019 #19

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

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

我想这是最简单和资源高效的方法。

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

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);
}
0赞 Kaplan 2/24/2021 #20

使用 lambda 仅删除几个相等条目
中的第一个

boolean[] done = {false};
String[] arr = Arrays.stream( foo ).filter( e ->
  ! (! done[0] && Objects.equals( e, item ) && (done[0] = true) ))
    .toArray(String[]::new);

可以删除条目null