如何将像[" a"," b"," c"]这样的集合转换为类似{" a&#34的地图;:0," b":1," c":2},其值为迭代次序。 在JDK8中是否有一个带流和收集器的衬里? 旧时尚的方式是这样的:
Collection<String> col = apiCall();
Map<String, Integer> map = new HashMap<>();
int pos = 0;
for (String s : collection) {
map.put(s, pos++);
}
答案 0 :(得分:6)
您不需要并行流,您可以使用地图的长度作为索引计数器:
collection.stream().forEach(i -> map.put(i, map.size() + 1));
答案 1 :(得分:6)
这是一种方法:
List<String> list = Arrays.asList("a", "b", "c");
Map<String, Integer> map =
IntStream.range(0, list.size())
.boxed()
.collect(toMap(i -> list.get(i), i -> i));
不一定是单行或短于简单循环,但如果您将toMap
更改为toConcurrentMap
,它确实可以使用并行流。
另请注意,这假设您有一个随机访问列表,而不是一般Collection
。如果你有Collection
,否则你不能做任何假设,除了按顺序迭代它并增加一个计数器之外你没有什么可做的。
<强>更新强>
OP已澄清输入为Collection
而非List
,因此上述内容并不适用。似乎我们对输入Collection
的假设很少。 OP已指定迭代顺序。使用顺序迭代器,元素将以某些顺序出现,但不能保证它。它可能会在不同的运行之间发生变化,甚至从一次迭代变为另一次迭代(尽管这在实践中是不常见的 - 除非修改了底层集合)。
如果需要保留确切的迭代顺序,我不相信有将其保留在结果Map
中的方法,而不是顺序迭代输入Collection
。
但是,如果确切的迭代顺序不重要,并且要求输出Map
具有每个输入元素的唯一值,则可以并行执行此类操作:
Collection<String> col = apiCall();
Iterator<String> iter = col.iterator();
Map<String, Integer> map =
IntStream.range(0, col.size())
.parallel()
.boxed()
.collect(toConcurrentMap(i -> { synchronized (iter) { return iter.next(); }},
i -> i));
现在远来自单行。我也不清楚它有多有用。 :-)但它确实表明它可以并行地做这样的事情。请注意,我们必须同步访问输入集合的迭代器,因为它将从多个线程调用。另请注意,这是迭代器的一种不寻常的用法,因为我们从不调用hasNext
,并且我们假设可以安全地调用next
完全由输入集合返回的次数size()
{ {1}}。
答案 2 :(得分:2)
基于maba’s answer,一般解决方案是:
collection.stream().forEachOrdered(i -> map.put(i, map.size()));
来自documentation of void forEachOrdered(Consumer<? super T> action)
:
此操作一次处理一个元素,如果存在,则按顺序处理。
这里的重要方面是,如果存在订单,它会保留订单,例如如果Collection
是SortedSet
或List
。这样的流称为有序流(不要与排序流混淆)。它可能会通过不同的线程调用使用者方法,但始终确保“一次一个”并保证线程安全。
当然,如果流是并行的,它将无法从并行执行中受益。
为了完整起见,这里的解决方案甚至可以在利用并行处理的并行流上工作,如果它们仍然有序:
stream.collect(HashMap::new, (m, i) -> m.put(i, m.size()),
(a, b) -> {int offset = a.size(); b.forEach((k, v) -> a.put(k, v + offset));});
答案 3 :(得分:1)
如果您不介意使用第三方库,我的cyclops-react lib具有所有JDK Collection类型的扩展名,附加了大量强大的操作符,因此您可以这样实现: -
length()
cyclops-react集合扩展非常渴望,所以使用我们的Stream扩展ReactiveSeq(它扩展了jOOλ的Seq,后者又是JDK的java.util.stream.Stream的扩展,它会获得更好的性能)也实现了反应流api)。
length()
答案 4 :(得分:1)
您可以在流中使用AtomicInteger作为索引:
Collection col = Arrays.asList("a", "b", "c");
AtomicInteger index = new AtomicInteger(0);
Map collectedMap = col.stream().collect(Collectors.toMap(Function.identity(), el -> index.getAndIncrement()));
System.out.println("collectedMap = " + collectedMap);
答案 5 :(得分:0)
尝试
int[] pos = { 0 };
list.forEach( a -> map.put(a, pos[0]++));