【问题标题】:How do I remove objects from an array in Java?如何从 Java 中的数组中删除对象?
【发布时间】:2010-09-11 20:49:33
【问题描述】:

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

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

我要怎么做才能删除/移除数组中所有等于"a"的字符串/对象?

【问题讨论】:

  • 您不能在 Java 中调整数组的大小。我假设您不想只是将元素归零,因为那将是微不足道的。是否要移动其他元素以消除间隙?
  • 这是微不足道的,现在我知道我可以做到。 ;) 谢谢!

标签: java arrays data-structures data-manipulation


【解决方案1】:

[如果你想要一些现成的代码,请滚动到我的“Edit3”(剪切后)。其余的留给后代。]

充实Dustman's idea

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。如果您想要一个大小完全符合要求的 new 数组,请改用它:

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

这将停止在你的堆中乱扔无用的空字符串数组,否则每次调用你的函数时这些数组都是newed。

cynicalman 的建议(参见 cmets)也将有助于解决堆乱扔垃圾问题,为了公平起见,我应该提一下:

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

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

【讨论】:

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

Java 8 中的替代方案:

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

【讨论】:

  • 这应该是公认的答案。虽然考虑到其他编程语言可以用更少和更清晰的代码来做到这一点并不漂亮......这是最好的 Java 必须提供而不依赖于另一个库。
  • 虽然简洁,但与 System.arraycopy 相比,这将是非常低效的。可能不应该在实际代码中这样做。
  • 如果 foo 是动态字符串变量,则会引发编译时错误 在封闭范围内定义的局部变量 foo 必须是最终的或有效的最终
  • 问题说明 “给定一个包含 n 个对象的数组” - 所以Stream.of(foo).filter(s -&gt; ! s.equals("a")).toArray() 就足够了。
  • 更多时候我只想remove the first几个相等的条目。
【解决方案3】:

使用Arrays.asList() 从数组中创建一个List,并在所有适当的元素上调用remove()。然后在“列表”上调用toArray() 再次返回数组。

性能不是特别好,但如果封装得当,以后总能更快地做一些事情。

【讨论】:

  • 重新评论:没关系,你很快就会习惯的。 :-) 我发布我的帖子是因为我不希望读者认为元素可以从 Arrays.asList() 的结果中删除(它是一个不可变列表),所以我认为一个示例可以解决这个问题。 :-)
  • 呃,我的意思是不可调整大小的列表(add() 和 remove() 不起作用)。 :-P 它仍然有一个可用的 set() 方法。 :-)
  • 虽然看起来很奇怪,但我的经验是这种方法的性能损失很小。
  • 这是怎么回事? @Chris 指出由 Arrays.asList() 生成的列表不支持 remove()。那么这个答案完全无效吗?看起来可能有些 cmets 被删除了,所以我不知道是否讨论过这个问题。
  • Chris 和 LarsH 都是正确的:数组支持的列表(换句话说,使用 Arrays.asList() 创建的列表)在结构上是不可变的,这完全使这个答案无效。然而,截至目前,我看到了 17 个赞成票。让人惊叹……
【解决方案4】:

你总是可以这样做的:

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

【讨论】:

    【解决方案5】:

    您可以使用外部库:

    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) 也很有用。
    【解决方案6】:

    见下方代码

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

    【讨论】:

      【解决方案7】:

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

      【讨论】:

      • 请不要这样做。它令人困惑、缓慢且容易出错。只需使用 System.arraycopy() 代替 - 尽管如果您要以这种方式操作数组,则必须跟踪长度这一事实的加分点。
      【解决方案8】:

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

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

      解决方案:使用集合。

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

      原因:您担心大小或希望控制内存分配

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

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

      原因:你有一个数组进来和一个数组出去——所以你想对一个数组进行操作

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

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

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

      原因:这是一个班级作业,您不被允许或由于某种原因您无权访问集合 api

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

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

      原因:你需要运行裸机

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

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

      您可能想要这样做的一个示例:单个基元数组(假设是 int 值)占用了您的 ram 的很大一部分——比如 50%! ArrayList 会强制将它们放入指向 Integer 对象的指针列表,这将使用该内存量的几倍。

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

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

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

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

      【讨论】:

        【解决方案9】:

        关于创建一个列表然后删除然后返回到一个数组的事情让我觉得是错误的。尚未测试,但我认为以下会表现更好。是的,我可能过度预优化。

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

        【讨论】:

          【解决方案10】:

          我意识到这是一篇很老的帖子,但这里的一些答案帮助了我,所以这是我的 tuppence'ha'penny 的价值!

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

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

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

          【讨论】:

          • 我觉得有必要发布这个,因为我经常使用无法工作的 sn-ps 代码,因为我错过了其他编码人员认为理所当然的事情。 GHad 指出了使我的代码正常工作的数组大小(感谢您明确表示)。尝试一些东西是学习的方式,如果这意味着我应该得到从 SO 获取代码并试图理解它如何/为什么工作的一切,那么就这样吧。作为一个无偿的业余爱好者,我不是某些人认为的 Java 天才!值得庆幸的是,大多数 SO 贡献者回答问题以帮助其他人编写更好的代码:为此,你值得感谢!
          【解决方案11】:

          初始数组

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

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

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

          【讨论】:

            【解决方案12】:

            编辑:

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

            原文:

            嗯……线

            array = list.toArray(array);
            

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

            如果您想避免这种情况,请使用新的数组作为 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));
            

            没有留下任何空值。

            【讨论】:

            • 不错的尝试,但没有雪茄。在您发布帖子之前,我发布了关于这个主题的编辑。因此,尽管您“在技术上是正确的”,但我不欣赏您试图让人们取代我的帖子。我只是觉得你应该知道这一点。
            • 这不是关于后置换,而是关于避免错误和危险代码。 Greetz GHad
            • 如果人们阅读我的整个帖子(包括两个附录),可以避免错误。如果人们只是不假思索地剪切和粘贴代码,那么他们应该得到他们所得到的一切。程序员得到报酬是因为他们锻炼了他们的大脑……我希望如此。 [继续]
            • [继续] 如果人们没有意识到通过使用哈希,项目会变得混乱,那么您的代码也是“危险的”。当然,会思考的程序员意识到了这一点,但如果你说我的代码很危险,因为人们不假思索地剪切和粘贴,那么你也只能这么说。
            • 当然,您对哈希的看法是正确的。由于您的第二次编辑清楚地表明了这一点,我一定是过度阅读了这一点。如前所述,只是想避免具有空值和重复的数组。您可以根据您的第二次编辑更改您的代码,并在 Edit3 中留下有关数组的评论。不想攻击你
            【解决方案13】:

            我对这个问题的一点贡献。

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

            }

            【讨论】:

            • 这是一个非常好的答案,尽管您出色的测试代码使它看起来比实际大得多。整个解决方案可能是一个单行数组复制(假设您使用相同的数组,如果您在使用数组而不是集合的金属附近工作,那么这就是您想要做的)。
            • 谢谢你,我写它是为了让读者可以看到一些幕后发生的事情并对其进行测试,现在我再看一遍 System.arraycopy(tmp1, 0, tmp2, 0, tmp2.长度) ;可以删除并替换为 for(i = 0; i
            【解决方案14】:

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

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

            【讨论】:

              【解决方案15】:

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

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

              【讨论】:

                【解决方案16】:

                将复制除索引为 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) );
                            }
                

                【讨论】:

                  【解决方案17】:

                  如果元素的顺序无关紧要。您可以在元素 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);
                  }
                  

                  【讨论】:

                    【解决方案18】:

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

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

                    可以删除null 条目

                    【讨论】:

                      【解决方案19】:

                      在字符串数组中

                      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

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

                      【讨论】:

                        【解决方案20】:

                        将 null 分配给数组位置。

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2023-01-16
                          • 2017-06-15
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2011-10-02
                          相关资源
                          最近更新 更多