4

I have a method which looks like this:

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

the exact same code would work if I replace List<String> list with String[] list, however, to avoid spaghetti code, I keep the single method, and when I need to call it on an array a, I do it like this: foo(Arrays.asList(a)).

I wonder if this is The Right Way.

Specifically,

  • What is the overhead of Arrays.asList()?
  • Is there a way to write a method which would accept both arrays and lists, just like the for loop does?

Thanks!

4

5 回答 5

6

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

但您可以执行以下操作:

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),...);
  }
}
于 2012-06-05T16:15:20.110 回答
2

openjdk的源代码,Arrays.asList

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

此外:

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

所以基本上所有这些都发生在分配中,所以开销应该可以忽略不计。

于 2012-06-05T16:19:04.943 回答
1

开销在于它将一个数组转换为一个列表——它如何做到这一点将取决于实现,它只需要履行合同。

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

于 2012-06-05T16:15:59.083 回答
1

请避免这种情况,我只使用并允许列表、集合和地图(就像 Joshua Bloch 告诉我们的那样)。无法合并这两种“集合类型”。

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

于 2012-06-05T16:16:15.977 回答
1

好问题。

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

现在,如果您查看 的源代码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();
  }
}
于 2012-06-05T16:37:28.800 回答