Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/382.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 什么';当我需要调整源代码列表时,addAll是最有效的等价物吗?_Java_Collections_Java 8 - Fatal编程技术网

Java 什么';当我需要调整源代码列表时,addAll是最有效的等价物吗?

Java 什么';当我需要调整源代码列表时,addAll是最有效的等价物吗?,java,collections,java-8,Java,Collections,Java 8,如果我想将一个列表添加到另一个列表中,我会调用target.adAll(源代码) 但如果我需要先处理列表中的每个值,该怎么办 我可以做类似的事情 for(String s: source) { target.add(s.toLowerCase()); } 或使用java 8: source.stream().map(x->x.toLowerCase()).forEachOrdered(target::add); 但不管怎样,我似乎失去了addAll的性能优势。执行此操作最有效的方法

如果我想将一个列表添加到另一个列表中,我会调用
target.adAll(源代码)

但如果我需要先处理列表中的每个值,该怎么办

我可以做类似的事情

for(String s: source) {
  target.add(s.toLowerCase());
}
或使用java 8:

source.stream().map(x->x.toLowerCase()).forEachOrdered(target::add);

但不管怎样,我似乎失去了
addAll
的性能优势。执行此操作最有效的方法是什么?

您可以使用来自的
collect()
模式,
collect()
相当于JDK的
map()

MutableList<String> source = Lists.mutable.with("A", "B", "C");
MutableList<String> target = source.collect(String::toLowerCase);
如果无法从
列表更改列表

List<String> source = Arrays.asList("A", "B", "C");
List<String> target = ListAdapter.adapt(source).collect(String::toLowerCase);
List<String> source = Arrays.asList("A", "B", "C");
List<String> target = new ArrayList<>(source);
target.replaceAll(String::toLowerCase);
上述解决方案可能更有效,也可能不更有效,但是,它们都预先确定了目标列表的大小


注意:我是Eclipse集合的贡献者。

那么,“addAll的性能优势是什么?”?最后,
addAll
必须将所有元素添加到目标
集合中。如果目标是
阵列列表
,主要好处是确保没有不必要的容量增加操作

但请注意,这是以创建临时数组为代价的,请参见。为了超过这一开销,您必须添加大量元素

如果我们要增加的元素超过目标公司当前的容量,那么增加操作是不可避免的。因此,
addAll
只提供了一个好处,如果您只需使用
add
,目标必须多次增加容量。由于容量提高了1.5倍,且容量等于或高于当前大小,我们必须添加至少超过其当前大小一半的元素,以预期不必要的容量增加操作

如果您真的认为这将是一个问题,很容易解决:

if(target instanceof ArrayList)
    ((ArrayList)target).ensureCapacity(target.size()+source.size());
source.stream().map(String::toLowerCase).forEachOrdered(target::add);
当然,在某些情况下,
add
的成本要高得多,例如,
CopyOnWriteArrayList
。对于此目标集合类型,首先通过
collect(Collectors.toList())
将数据收集到
列表中,然后通过
addAll
进行收集可能是有益的。或者创建一个简单的惰性
集合
,作为中间步骤:

public static <T> Collection<T> lazyCollection(Supplier<? extends Stream<T>> s) {
    return new AbstractCollection<T>() {
        public Iterator<T> iterator() { return s.get().iterator(); }
        public int size() { return (int)s.get().count(); }
        public Object[] toArray() { return s.get().toArray(); }
    };
}

如果一个集合在获取一个迭代器之前先请求
size()
,那么这种方法会遇到两次对流求值的问题,但好吧,没有标准集合会这样做。他们要么使用迭代器而不依赖于预测的大小,要么求助于
toArray()
,比如
ArrayList.addAll
CopyOnWriteArrayList.addAll
do。

我想JIT应该能够将第二段代码转换为第一段。顺便说一句,你能做的再好不过了。谢谢你,这是一个很好的解释。你能解释一下为什么LinkedList.addAll实现调用源集合上的数组吗?这让人怀疑我是否还缺少其他一些“性能魔法”(如L2缓存),因为我看不出有任何其他原因。如果源代码是线程安全的集合,那么在单个调用中获取所有元素可能是原子的,这取决于源代码的类型,从而确保某些组合的安全性,但由于没有指定这样的行为,因此没有理由实现它。此外,如果向其自身添加一个列表,这可以确保其工作,但另一方面,有一种廉价的方法来测试这种特殊情况,因此不必以这种方式处理所有源。一般来说,请求完整数组表明对源代码的迭代器有一些不信任,但我们不确定是什么导致了这个决定。
public static <T> Collection<T> lazyCollection(Supplier<? extends Stream<T>> s) {
    return new AbstractCollection<T>() {
        public Iterator<T> iterator() { return s.get().iterator(); }
        public int size() { return (int)s.get().count(); }
        public Object[] toArray() { return s.get().toArray(); }
    };
}
target.addAll(lazyCollection(() -> source.stream().map(String::toLowerCase)));