Hi i am trying to convert the following code using java 8 stream API.
private Point3d findClosestNodeToParentStartNode1(List<Point3d> points,Point3d parentStartVertex)
{
TreeMap<Double, Point3d> distanceMap = new TreeMap<Double, Point3d>();
for (Point3d point : points) {
distanceMap.put(parentStartVertex.distanceTo(point), point);
}
return distanceMap.firstEntry().getValue();
}
I am trying to do something like
Map<Double, Point3d> result = points.stream().collect(Collectors.toMap(parentStartVertex.distanceTo(point->point) , point -> point));
TreeMap<Double, Point3d> distanceMap = new TreeMap<>(result);
return distanceMap.firstEntry().getValue();
CodePudding user response:
Your code won't even compile as the key function is wrong. You also have an intermediate step which isn't needed, you can directly use the toMap
function which takes 4 arguments and return a TreeMap
directly.
Something like this should work
TreeMap <Double, Point3d> distanceMap = points.stream().collect(
Collectors.toMap(
parentStartVertex::distanceTo,
Function.identity(),
(k1, k2) -> k2,
TreeMap::new));
return distanceMap.firstEntry().getValue();
For what is worth I don't think there is anything wrong with the forEach
approach and in this case it might even be easier to read (and might even be faster due to less overhead).
TreeMap<Double, Point3d> distanceMap = new TreeMap<Double, Point3d>();
points.forEach( point -> distanceMap.put(parentStartVertex.distanceTo(point), point));
return distanceMap.firstEntry().getValue();
CodePudding user response:
If you want to find the smallest distance (min
) between the target parentStartVertex
and the individual points in the list, then you can use Stream#min
or Collections#min
with a custom Comparator
comparing based on the distance between parentStartVertex
and the individual items. Pick one of the solutions below:
/**
* Returns null in case the list `points` is empty
*/
private Point3d findClosestNodeToParentStartNode1(List<Point3d> points, Point3d parentStartVertex) {
return points.stream()
.min(Comparator.comparingDouble(parentStartVertex::distanceTo))
.orElse(null);
}
/**
* Throws NoSuchElementException in case the list `points` is empty
*/
private Point3d findClosestNodeToParentStartNode1(List<Point3d> points, Point3d parentStartVertex) {
return Collections.min(
points,
Comparator.comparingDouble(parentStartVertex::distanceTo));
}
CodePudding user response:
EDIT: the solution is not correct as explained in the comment, still by reading the implementation of toMap() with two arguments in the source code the solution becomes rather obvious with a few minor adjustments.
The default Map Implementation the function will use is HashMap, you just need to provide the map implementation you need as last argument like:
Map<Double, Point3d> result = points.stream().collect(Collectors.toMap(parentStartVertex.distanceTo(point->point) , point -> point, Treemap::new));
If you chek out the source code you'll see that the toMap method you used just calls the overload method beneath it with the defaul HashMap implementation:
/**
* Returns a {@code Collector} that accumulates elements into a
* {@code Map} whose keys and values are the result of applying the provided
* mapping functions to the input elements.
*
* <p>If the mapped keys contains duplicates (according to
* {@link Object#equals(Object)}), an {@code IllegalStateException} is
* thrown when the collection operation is performed. If the mapped keys
* may have duplicates, use {@link #toMap(Function, Function, BinaryOperator)}
* instead.
*
* @apiNote
* It is common for either the key or the value to be the input elements.
* In this case, the utility method
* {@link java.util.function.Function#identity()} may be helpful.
* For example, the following produces a {@code Map} mapping
* students to their grade point average:
* <pre>{@code
* Map<Student, Double> studentToGPA
* students.stream().collect(toMap(Functions.identity(),
* student -> computeGPA(student)));
* }</pre>
* And the following produces a {@code Map} mapping a unique identifier to
* students:
* <pre>{@code
* Map<String, Student> studentIdToStudent
* students.stream().collect(toMap(Student::getId,
* Functions.identity());
* }</pre>
*
* @implNote
* The returned {@code Collector} is not concurrent. For parallel stream
* pipelines, the {@code combiner} function operates by merging the keys
* from one map into another, which can be an expensive operation. If it is
* not required that results are inserted into the {@code Map} in encounter
* order, using {@link #toConcurrentMap(Function, Function)}
* may offer better parallel performance.
*
* @param <T> the type of the input elements
* @param <K> the output type of the key mapping function
* @param <U> the output type of the value mapping function
* @param keyMapper a mapping function to produce keys
* @param valueMapper a mapping function to produce values
* @return a {@code Collector} which collects elements into a {@code Map}
* whose keys and values are the result of applying mapping functions to
* the input elements
*
* @see #toMap(Function, Function, BinaryOperator)
* @see #toMap(Function, Function, BinaryOperator, Supplier)
* @see #toConcurrentMap(Function, Function)
*/
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}
/**
* Returns a {@code Collector} that accumulates elements into a
* {@code Map} whose keys and values are the result of applying the provided
* mapping functions to the input elements.
*
* <p>If the mapped
* keys contains duplicates (according to {@link Object#equals(Object)}),
* the value mapping function is applied to each equal element, and the
* results are merged using the provided merging function. The {@code Map}
* is created by a provided supplier function.
*
* @implNote
* The returned {@code Collector} is not concurrent. For parallel stream
* pipelines, the {@code combiner} function operates by merging the keys
* from one map into another, which can be an expensive operation. If it is
* not required that results are merged into the {@code Map} in encounter
* order, using {@link #toConcurrentMap(Function, Function, BinaryOperator, Supplier)}
* may offer better parallel performance.
*
* @param <T> the type of the input elements
* @param <K> the output type of the key mapping function
* @param <U> the output type of the value mapping function
* @param <M> the type of the resulting {@code Map}
* @param keyMapper a mapping function to produce keys
* @param valueMapper a mapping function to produce values
* @param mergeFunction a merge function, used to resolve collisions between
* values associated with the same key, as supplied
* to {@link Map#merge(Object, Object, BiFunction)}
* @param mapSupplier a function which returns a new, empty {@code Map} into
* which the results will be inserted
* @return a {@code Collector} which collects elements into a {@code Map}
* whose keys are the result of applying a key mapping function to the input
* elements, and whose values are the result of applying a value mapping
* function to all input elements equal to the key and combining them
* using the merge function
*
* @see #toMap(Function, Function)
* @see #toMap(Function, Function, BinaryOperator)
* @see #toConcurrentMap(Function, Function, BinaryOperator, Supplier)
*/
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier) {
BiConsumer<M, T> accumulator
= (map, element) -> map.merge(keyMapper.apply(element),
valueMapper.apply(element), mergeFunction);
return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
}