【问题标题】:How do I write a method which would work on both lists and arrays?如何编写一个适用于列表和数组的方法?
【发布时间】:2023-03-17 13:00:01
【问题描述】:

我有一个看起来像这样的方法:

void foo (List<String> list, ...) {
  ...
  for (String s : list) { // this is the only place where `list` is used
    ...
  }
  ...
}

如果我将List&lt;String&gt; list 替换为String[] list,完全相同的代码将起作用,但是,为了避免意大利面条式代码,我保留了单一方法,当我需要在数组a 上调用它时,我会这样做像这样:foo(Arrays.asList(a))

我想知道这是否是正确的方式。

具体来说,

  • Arrays.asList() 的开销是多少?
  • 有没有办法编写一个可以同时接受数组和列表的方法,就像 for 循环一样?

谢谢!

【问题讨论】:

标签: java arrays list


【解决方案1】:

Arrays.asList() 的开销很小。没有真正的方法可以为Listarrays 实现一种方法。

但您可以执行以下操作:

void foo (List<String> list, ...) {
  ...
  for (String s : list) { // this is the only place where *list* is used
    ...
  }
  ...
}

void foo (String[] arr, ...) {
  if ( arr != null ) {
      foo(Arrays.asList(arr),...);
  }
}

【讨论】:

  • 如果类型是固定的就可以了。但是,如果您编写通用代码,您将以 foo(T[]) 方法结束。你不应该那样做。曾经尝试过创建一个通用数组吗?! :-D
【解决方案2】:

来自openjdkArrays.asList的源码:

public static <T> List<T> asList(T... a) {
   return new ArrayList<>(a);
}

进一步:

ArrayList(E[] array) {
   if (array==null)
      throw new NullPointerException();
   a = array;
}

所以基本上所有的事情都发生在一个分配中,所以开销应该可以忽略不计。

【讨论】:

  • ArrayListArrays 中的私有内部类,而不是 java.util.ArrayListT[] 不是 Collection
  • @Banthar:谢谢,我已经编辑了我的答案。我想知道为什么他们给这个内部类和另一个 ArrayList 类命名相同。
【解决方案3】:

开销在于它将数组转换为列表-如何它将取决于实现,它只需要履行合同。

IMO 如果您担心潜在的运行时开销,您应该编写两种方法:这是 Java 的本质;方法具有类型签名,必须遵守。

【讨论】:

    【解决方案4】:

    请避免这种情况,我只使用并允许列表、集合和地图 (like Joshua Bloch told us)。没有办法合并这两种“集合类型”。

    另一种方法是使用guava(迭代器/迭代器)。因此,您可以在没有深度副本的情况下迭代您的集合。

    【讨论】:

      【解决方案5】:

      好问题。

      这是一种非常常见的情况,通常通过编写两个单独的方法来处理。然而,重复代码确实是个坏主意,每当你发现自己在重复代码时,你应该开始寻找机会来更好地分解你的代码。 (正如你现在所做的那样!)

      现在,如果您查看java.util.Arrays 的源代码,您会注意到Arrays.asList 重新运行私有内部类Arrays.ArrayList 的实例,它只是普通数组的薄包装,并将所有相关的方法调用委托给它。 (这被称为数据结构的投影或视图。)因此产生的开销是微不足道的(除非您正在努力提取最后一点性能),并且在我看来,您应该继续使用这种方法而不必担心关于性能。


      我个人使用的解决方案如下。

      我的个人实用程序中有一个名为 RichIterable 的类。顾名思义,该类包含Iterable,并提供了一些尚不存在的其他有用方法。该类还有一个工厂方法,可以从数组中创建一个RichIterable。这是类定义。

      public class RichIterable<A> implements Iterable<A> {
        private Iterable<A> xs;
      
        private RichIterable(Iterable<A> xs) {
          this.xs = xs;
        }
      
        public static <A> RichIterable<A> from(Iterable<A> xs) {
          if (xs instanceof RichIterable) {
            return (RichIterable<A>) xs;
          } else {
            return new RichIterable<A>(xs);
          }
        }
      
        public static <A> RichIterable<A> from(final Enumeration<A> xs) {
          Iterable<A> iterable = new Iterable<A>() {
            @Override
            public Iterator<A> iterator() {
              return new Iterator<A>() {
                @Override
                public boolean hasNext() {
                  return xs.hasMoreElements();
                }
      
                @Override
                public A next() {
                  return xs.nextElement();
                }
      
                @Override
                public void remove() {
                  throw new UnsupportedOperationException(
                    "Cannot remove an element from an enumeration.");
                }
              };
            }
          };
          return RichIterable.from(iterable);
        }
      
        public static <A> RichIterable<A> from(final A[] xs) {
          Iterable<A> iterable = new Iterable<A>() {
            @Override
            public Iterator<A> iterator() {
              return new Iterator<A>() {
                private int i = 0;
      
                @Override
                public boolean hasNext() {
                  return i < xs.length;
                }
      
                @Override
                public A next() {
                  A x = xs[i];
                  i++;
                  return x;
                }
      
                @Override
                public void remove() {
                  throw new UnsupportedOperationException(
                    "Cannot remove an element from an array.");
                }
              };
            }
          };
          return RichIterable.from(iterable);
        }
      
        public boolean isEmpty() {
          if (xs instanceof Collection) {
            return ((Collection) xs).isEmpty();
          }
          for (A x : xs) {
            return false;
          }
          return true;
        }
      
        public int size() {
          if (xs instanceof Collection) {
            return ((Collection) xs).size();
          }
          int size = 0;
          for (A x : xs) {
            size++;
          }
          return size;
        }
      
        public ArrayList<A> toArrayList() {
          ArrayList<A> ys = new ArrayList<A>();
          for (A x : xs) {
            ys.add(x);
          }
          return ys;
        }
      
        public <B> RichIterable<B> map(F1<A, B> f) {
          List<B> ys = new ArrayList<B>();
          for (A x : xs) {
            ys.add(f.apply(x));
          }
          return RichIterable.from(ys);
        }
      
        public RichIterable<A> filter(F1<A, Boolean> pred) {
          List<A> ys = new ArrayList<A>();
          Arrays.asList();
          for (A x : xs) {
            if (pred.apply(x)) {
              ys.add(x);
            }
          }
          return RichIterable.from(ys);
        }
      
        public boolean exists(F1<A, Boolean> pred) {
          for (A x : xs) {
            if (pred.apply(x)) {
              return true;
            }
          }
          return false;
        }
      
        public boolean forall(F1<A, Boolean> pred) {
          for (A x : xs) {
            if (!pred.apply(x)) {
              return false;
            }
          }
          return true;
        }
      
        public Maybe<A> find(F1<A, Boolean> pred) {
          for (A x : xs) {
            if (pred.apply(x)) {
              return Just.of(x);
            }
          }
          return Nothing.value();
        }
      
        public String mkString(String beg, String sep, String end) {
          Iterator<A> i = xs.iterator();
          if (!i.hasNext()) {
            return beg + end;
          }
          StringBuilder sb = new StringBuilder();
          sb.append(beg);
          while (true) {
            A e = i.next();
            sb.append(e.toString());
            if (!i.hasNext()) {
              return sb.append(end).toString();
            }
            sb.append(sep);
          }
        }
      
        public String mkString(String sep) {
          return mkString("", sep, "");
        }
      
        public String mkString() {
          return this.mkString(", ");
        }
      
        public Iterable<A> getRaw() {
          return xs;
        }
      
        @Override
        public Iterator<A> iterator() {
          return xs.iterator();
        }
      }
      

      【讨论】:

      • JFYI:我建议你改用番石榴,它的作用相同,甚至更多。使用这个库的好处:你不必维护代码,番石榴是一个开放的库。所以其他第三方模块可以使用该库。
      • @MarcelJaeschke,番石榴很好,但功能不足以满足我的口味。
      • 我也有自己的 util lib,但我删除了番石榴覆盖的所有内容。所以我需要维护的代码更少。顺便说一句:我认为 guave 中的 map(transform) 和 filter 方法更好(不要创建副本)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-16
      • 1970-01-01
      • 1970-01-01
      • 2021-09-08
      • 2011-12-30
      • 2018-11-22
      相关资源
      最近更新 更多