It seems what you want is just:
ConcurrentMap<String, Model> modelsMap =
models.parallelStream()
.collect(toConcurrentMap(Model::getId, identity()));
This creates a ConcurrentMap from the list of models by collecting it with Collectors.toConcurrentMap. The key mapper is the function that returns the name of the model and the value mapper is the identity function.
Note that this will throws an exception is case of duplicate ids.
If you want to create the list of names along with this Map in the same Stream pipeline, you could use the pairing collector written in this answer and hold the result inside a custom Pair class. The Map will be the first element of the pair and the list of names will be the second.
Pair<Map<String, Model>, List<String>> pair =
models.parallelStream()
.collect(pairing(
toConcurrentMap(Model::getId, identity()),
mapping(Model::getName, toList()),
Pair::new)
);
static <T, A1, A2, R1, R2, R> Collector<T, ?, R> pairing(Collector<T, A1, R1> c1,
Collector<T, A2, R2> c2, BiFunction<R1, R2, R> finisher) {
EnumSet<Characteristics> c = EnumSet.noneOf(Characteristics.class);
c.addAll(c1.characteristics());
c.retainAll(c2.characteristics());
c.remove(Characteristics.IDENTITY_FINISH);
return Collector.of(() -> new Object[] {c1.supplier().get(), c2.supplier().get()},
(acc, v) -> {
c1.accumulator().accept((A1)acc[0], v);
c2.accumulator().accept((A2)acc[1], v);
},
(acc1, acc2) -> {
acc1[0] = c1.combiner().apply((A1)acc1[0], (A1)acc2[0]);
acc1[1] = c2.combiner().apply((A2)acc1[1], (A2)acc2[1]);
return acc1;
},
acc -> {
R1 r1 = c1.finisher().apply((A1)acc[0]);
R2 r2 = c2.finisher().apply((A2)acc[1]);
return finisher.apply(r1, r2);
}, c.toArray(new Characteristics[c.size()]));
}
with the following Pair class:
public class Pair<T, U> {
private final T first;
private final U second;
public Pair(T first, U second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public U getSecond() {
return second;
}
}