将数组 A[] 转换为 ArrayList<B> 的有效且最短的方法

Effective and shortest way to convert array A[] to ArrayList<B>

我有2个类

甲:

  public class A {
    private int intValue;
    private String stringValue;

    public A(int intValue, String stringValue) {
      this.intValue = intValue;
      this.stringValue = stringValue;
    }

    int getIntValue() {
      return intValue;
    }

    String getStringValue() {
      return stringValue;
    }
  }

和乙:

  public class B {
    private int intValue;
    private String stringValue;

    B(int intValue, String stringValue) {
      this.intValue = intValue;
      this.stringValue = stringValue;
    }

    int getIntValue() {
      return intValue;
    }

    String getStringValue() {
      return stringValue;
    }
  }

和一些相当大的 A 对象数组。

我想有效地将​​ A[] 转换为 ArrayList<B>。我知道有几种方法可以做到这一点:

final A[] array = {new A(1, "1"), new A(2, "2")/*...*/};

// 1 - old-school
final List<B> list0 = new ArrayList<>(array.length);
for (A a : array) {
  list0.add(new B(a.getIntValue(), a.getStringValue()));
}

// 2 - pretty mush same as 1
final List<B> list1 = new ArrayList<>(array.length);
Arrays.stream(array).forEach(a -> list1.add(new B(a.getIntValue(), a.getStringValue())));

// 3 - lambda-style
final List<B> list2 = Arrays.stream(array).map(a -> new B(a.getIntValue(), a.getStringValue())).collect(Collectors.toList());

// 4 - lambda-style with custom Collector
final List<B> list3 = Arrays.stream(array)
    .map(a -> new B(a.getIntValue(), a.getStringValue()))
    .collect(Collector.of((Supplier<List<B>>)() -> new ArrayList(array.length), List::add, (left, right) -> {
      left.addAll(right);
      return left;
    }));

AFAIK 1 是最有效的。但是有了 java 8 个特性,它可以变得更短。 2 与 1 几乎相同,但 foreach 循环被流 foreach 取代。不确定它的有效性。 3 是最短的方法,但是默认 Collectors.toList() 收集器使用默认 ArrayList::new 构造函数,这意味着如果我们有相当大的初始数组, ArrayList 中的数组将至少调整一次大小。所以效率不高。而 4,据我所知(虽然从未使用过这种方式)与 3 几乎相同,但在 ArrayList 中为数组分配了单个内存。但是长得丑

所以,我的问题是。我对这 4 种方法及其有效性的看法是否正确?还有其他简短有效的方法吗?

您的#4 可以写得更短一些:

final List<B> list4 = Arrays.stream(array)
    .map(a -> new B(a.getIntValue(), a.getStringValue()))
    .collect(toCollection(() -> new ArrayList<>(array.length)));

您也可以在单独的方法中提取映射:

final List<B> list4 = Arrays.stream(array)
    .map(SomeClass::mapA2B)
    .collect(toCollection(() -> new ArrayList<>(array.length)));    

private static B mapA2B(A a) {
  return new B(a.getIntValue(), a.getStringValue());
}

如果您对最高效的解决方案感兴趣,可以考虑

List<B> list=Arrays.asList(Arrays.stream(array)
    .map(a -> new B(a.getIntValue(), a.getStringValue()))
    .parallel().toArray(B[]::new));

collect 不同,它使用 Collector 抽象,toArrayStream 的内在操作。由于 Arrays.asList 返回的流具有已知大小,因此实现将创建一个目标数组并在并行处理时写入该数组的不同区域。相比之下,collect会创建多个列表,并行处理它们,然后合并它们,这不适合像这种简单的操作。

即使是单线程使用,它也可能表现更好,因为它只是填充一个数组(“在原始金属上工作”)然后创建一个轻量级的 List 视图。

这个想法是 this comment made by Brian Goetz

的直接结果