【问题标题】:Effective and shortest way to convert array A[] to ArrayList<B>将数组 A[] 转换为 ArrayList<B> 的有效且最短的方法
【发布时间】:2015-03-12 16:07:06
【问题描述】:

我有两节课

答:

  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&lt;B&gt;。我知道有几种方法可以做到这一点:

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 种方法及其有效性是否正确?有没有其他简单有效的方法来做到这一点?

【问题讨论】:

  • @a_horse_with_no_name, Arrays.asList() 只将A[] 转换为List&lt;A&gt;,这不是我想要的
  • @a_horse_with_no_name 对于可以附加到或从中删除的列表,new ArrayList&lt;&gt;(Arrays.asList())

标签: java java-8


【解决方案1】:

您的#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());
}

【讨论】:

  • 哇,谢谢!这实际上很短且易于阅读:)
【解决方案2】:

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

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的直接结果

【讨论】:

  • 谢谢!但不幸的是我不能使用parallel 流。对于普通的stream(),AFAIK 也一样。而Arrays.asList() 回报不完全ArrayList 我需要:)
  • 是的,如果您的要求是准确地获得 ArrayList 而不仅仅是 List(即以后确实需要更改其大小的能力),那将无济于事。对于顺序流,如果您已经使用自定义 Supplier 返回具有正确容量的 ArrayList,则影响不会那么大。
猜你喜欢
  • 2012-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-18
  • 2022-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多