Last active
June 3, 2020 21:48
-
-
Save nipafx/74ac912658f0e11e9057 to your computer and use it in GitHub Desktop.
Find a unique element in a stream without intention mismatch (http://blog.codefx.org/java/beware-stream-findfirst-findany/)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.codefx.lab.stream; | |
import java.util.Collection; | |
import java.util.Objects; | |
import java.util.Optional; | |
import java.util.function.BinaryOperator; | |
import java.util.function.Supplier; | |
/** | |
* Finds a certain customer in a collection of customers. | |
* <p/> | |
* All containing code is released into the public domain via | |
* <a href="http://creativecommons.org/publicdomain/zero/1.0/">CC0 1.0</a> | |
* by <a href="mailto:nipa@codefx.org"> Nicolai Parlog</a>. | |
* <p> | |
* See <a href="http://blog.codefx.org/java/beware-stream-findfirst-findany/">this post</a> | |
* to read up what this is about. | |
*/ | |
public class FindFirstFindAnyReduceToOnlyElement { | |
private Collection<Customer> customers; | |
// FINDING | |
public Optional<Customer> findCustomer_Loop(String customerId) { | |
for (Customer customer : customers) | |
if (customer.getId().equals(customerId)) | |
return Optional.of(customer); | |
return Optional.empty(); | |
} | |
public Optional<Customer> findCustomer_Stream(String customerId) { | |
return customers.stream() | |
.filter(customer -> customer.getId().equals(customerId)) | |
.findFirst(); | |
} | |
public Optional<Customer> findOnlyCustomer_Loop(String customerId) { | |
boolean foundOneCustomer = false; | |
Customer foundCustomer = null; | |
for (Customer customer : customers) | |
if (customer.getId().equals(customerId)) | |
if (!foundOneCustomer) { | |
foundOneCustomer = true; | |
foundCustomer = customer; | |
} else | |
throw new DuplicateCustomerException(); | |
return foundOneCustomer ? Optional.of(foundCustomer) : Optional.empty(); | |
} | |
public Optional<Customer> findOnlyCustomer_StreamWithManualException(String customerId) { | |
return customers.stream() | |
.filter(customer -> customer.getId().equals(customerId)) | |
.reduce((element, otherElement) -> { | |
throw new DuplicateCustomerException(); | |
}); | |
} | |
public Optional<Customer> findOnlyCustomer_Stream(String customerId) { | |
return customers.stream() | |
.filter(customer -> customer.getId().equals(customerId)) | |
.reduce(toOnlyElementThrowing(DuplicateCustomerException::new)); | |
} | |
// THE FOLLOWING WOULD GO INTO SOME UTILITY CLASS | |
public static <T> BinaryOperator<T> toOnlyElement() { | |
return toOnlyElementThrowing(IllegalArgumentException::new); | |
} | |
public static <T, E extends RuntimeException> BinaryOperator<T> toOnlyElementThrowing( | |
Supplier<E> exception) { | |
return (element, otherElement) -> { | |
throw exception.get(); | |
}; | |
} | |
// INNER CLASSES | |
public static class Customer { | |
private final String id; | |
private Customer(String id) { | |
this.id = Objects.requireNonNull(id); | |
} | |
private String getId() { | |
return id; | |
} | |
} | |
public static class DuplicateCustomerException extends RuntimeException { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.codefx.lab.stream; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.Optional; | |
import java.util.Set; | |
import java.util.function.BiConsumer; | |
import java.util.function.BinaryOperator; | |
import java.util.function.Function; | |
import java.util.function.Supplier; | |
import java.util.stream.Collector; | |
import static java.util.Objects.requireNonNull; | |
/** | |
* Finds a certain customer in a collection of customers. | |
* <p/> | |
* All containing code is released into the public domain via | |
* <a href="http://creativecommons.org/publicdomain/zero/1.0/">CC0 1.0</a> | |
* by <a href="mailto:nipa@codefx.org">Nicolai Parlog</a>. | |
*/ | |
public class FindFirstFindAnyCollectToOnlyElement { | |
private Collection<Customer> customers; | |
// FINDING | |
public Optional<Customer> findCustomer_Loop(String customerId) { | |
for (Customer customer : customers) | |
if (customer.getId().equals(customerId)) | |
return Optional.of(customer); | |
return Optional.empty(); | |
} | |
public Optional<Customer> findCustomer_Stream(String customerId) { | |
return customers.stream() | |
.filter(customer -> customer.getId().equals(customerId)) | |
.findFirst(); | |
} | |
public Optional<Customer> findOnlyCustomer_Loop(String customerId) { | |
boolean foundOneCustomer = false; | |
Customer foundCustomer = null; | |
for (Customer customer : customers) | |
if (customer.getId().equals(customerId)) | |
if (!foundOneCustomer) { | |
foundOneCustomer = true; | |
foundCustomer = customer; | |
} else | |
throw new DuplicateCustomerException(); | |
return foundOneCustomer ? Optional.of(foundCustomer) : Optional.empty(); | |
} | |
public Optional<Customer> findOnlyCustomer_StreamWithDefaultException(String customerId) { | |
return customers.stream() | |
.filter(customer -> customer.getId().equals(customerId)) | |
.collect(onlyElement()); | |
} | |
public Optional<Customer> findOnlyCustomer_StreamWithSpecificException(String customerId) { | |
return customers.stream() | |
.filter(customer -> customer.getId().equals(customerId)) | |
.collect(onlyElementThrowing(DuplicateCustomerException::new)); | |
} | |
// THE FOLLOWING WOULD GO INTO SOME UTILITY CLASS | |
public static <T> Collector<T, ?, Optional<T>> onlyElement() { | |
return onlyElementThrowing(IllegalArgumentException::new); | |
} | |
public static <T, E extends RuntimeException> Collector<T, ?, Optional<T>> onlyElementThrowing( | |
Supplier<E> exception) { | |
return new ToOnlyElementCollector<>(exception); | |
} | |
private static class ToOnlyElementCollector<T, EX extends RuntimeException> | |
implements Collector<T, Object[], Optional<T>> { | |
private final Supplier<EX> duplicateElementException; | |
private ToOnlyElementCollector(Supplier<EX> duplicateElementException) { | |
this.duplicateElementException = requireNonNull( | |
duplicateElementException, "The argument 'duplicateElementException' must not be null."); | |
} | |
@Override | |
public Supplier<Object[]> supplier() { | |
return () -> new Object[1]; | |
} | |
@Override | |
public BiConsumer<Object[], T> accumulator() { | |
return (container, element) -> { | |
if (container[0] == null) | |
container[0] = element; | |
else | |
throw duplicateElementException.get(); | |
}; | |
} | |
@Override | |
public BinaryOperator<Object[]> combiner() { | |
return (container1, container2) -> { | |
if (container1[0] != null && container2[0] != null) | |
throw duplicateElementException.get(); | |
else if (container1[0] != null) | |
return container1; | |
else if (container2[0] != null) | |
return container2; | |
// return any container | |
return container1; | |
}; | |
} | |
@Override | |
@SuppressWarnings("unchecked") | |
public Function<Object[], Optional<T>> finisher() { | |
return container -> Optional.ofNullable((T) container[0]); | |
} | |
@Override | |
public Set<Characteristics> characteristics() { | |
return Collections.singleton(Characteristics.UNORDERED); | |
} | |
} | |
// INNER CLASSES | |
public static class Customer { | |
private final String id; | |
private Customer(String id) { | |
this.id = requireNonNull(id); | |
} | |
private String getId() { | |
return id; | |
} | |
} | |
public static class DuplicateCustomerException extends RuntimeException { | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment