Last active
February 16, 2019 18:18
-
-
Save tlinkowski/bceba241e670cb6efeeea4490e363591 to your computer and use it in GitHub Desktop.
Accumulative: Custom Java Collectors Made Easy
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
/* | |
* Copyright 2019 Tomasz Linkowski. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package java.util.stream; | |
import java.util.function.Supplier; | |
import java.util.stream.Collector; | |
/** | |
* A standard contract for an intermediate accumulation type of the reduction operation, | |
* as defined in {@link Collector}. | |
* | |
* <p>The intermediate accumulation type also goes by the name of "mutable result container". | |
* | |
* @param <T> the type of input elements to the reduction operation | |
* @param <A> the intermediate accumulation type that is a subtype of {@link Accumulative} | |
* @param <R> the result type of the reduction operation | |
* | |
* @see Collector#of(Supplier, Collector.Characteristics...) | |
*/ | |
public interface Accumulative<T, A extends Accumulative<T, A, R>, R> { | |
/** | |
* Folds a value into this instance. | |
* | |
* <p>Method reference target for {@link Collector#accumulator()}. | |
* | |
* @param t value to fold | |
*/ | |
void accumulate(T t); | |
/** | |
* Accepts another partial result and merges it with this instance. The | |
* combine method may fold state from one partial result into the other and | |
* return that, or may return a new result container. | |
* | |
* <p>Method reference target for {@link Collector#combiner()}. | |
* | |
* @param other another partial result to combine | |
* @return a combined result | |
*/ | |
A combine(A other); | |
/** | |
* Performs the final transformation from the intermediate accumulation type | |
* {@code A} to the final result type {@code R}. | |
* | |
* <p>Method reference target for {@link Collector#finisher()}. | |
* | |
* @return the final result | |
*/ | |
R finish(); | |
} |
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
/* | |
* Copyright 2019 Tomasz Linkowski. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package java.util.stream; | |
import java.util.function.Supplier; | |
import java.util.stream.Collector; | |
public interface Collector<T, A, R> { | |
/** | |
* Returns a new {@code Collector} described by the given {@code supplier} of an {@link Accumulative} instance. | |
* | |
* @param supplier The supplier function for the new collector | |
* @param characteristics The collector characteristics for the new collector | |
* @param <T> The type of input elements for the new collector | |
* @param <A> The intermediate accumulation type of the new collector which is a subtype of {@link Accumulative} | |
* @param <R> The final result type of the new collector | |
* @throws NullPointerException if any argument is null | |
* @return the new {@code Collector} | |
*/ | |
static <T, A extends Accumulative<T, A, R>, R> Collector<T, ?, R> of( | |
Supplier<A> supplier, Collector.Characteristics... characteristics) { | |
return Collector.of(supplier, A::accumulate, A::combine, A::finish, characteristics); | |
} | |
} |
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
import java.util.Optional; | |
import java.util.function.BinaryOperator; | |
import java.util.function.Consumer; | |
import java.util.stream.Collector; | |
public class Collectors { | |
public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op) { | |
class OptionalBox implements Accumulative<T, OptionalBox, Optional<T>> { | |
T value = null; | |
boolean present = false; | |
@Override | |
public void accumulate(T t) { | |
if (present) { | |
value = op.apply(value, t); | |
} else { | |
value = t; | |
present = true; | |
} | |
} | |
@Override | |
public OptionalBox combine(OptionalBox other) { | |
if (other.present) accumulate(other.value); | |
return this; | |
} | |
@Override | |
public Optional<T> finish() { | |
return Optional.ofNullable(value); | |
} | |
} | |
return Collector.of(OptionalBox::new); | |
} | |
} |
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
public class Collectors { | |
public static <T, R1, R2, R> | |
Collector<T, ?, R> teeing(Collector<? super T, ?, R1> downstream1, | |
Collector<? super T, ?, R2> downstream2, | |
BiFunction<? super R1, ? super R2, R> merger) { | |
return teeing0(downstream1, downstream2, merger); | |
} | |
private static <T, A1, A2, R1, R2, R> | |
Collector<T, ?, R> teeing0(Collector<? super T, A1, R1> downstream1, | |
Collector<? super T, A2, R2> downstream2, | |
BiFunction<? super R1, ? super R2, R> merger) { | |
Supplier<A1> c1Supplier = downstream1.supplier(); | |
Supplier<A2> c2Supplier = downstream2.supplier(); | |
BiConsumer<A1, ? super T> c1Accumulator = downstream1.accumulator(); | |
BiConsumer<A2, ? super T> c2Accumulator = downstream2.accumulator(); | |
BinaryOperator<A1> c1Combiner = downstream1.combiner(); | |
BinaryOperator<A2> c2Combiner = downstream2.combiner(); | |
Function<A1, R1> c1Finisher = downstream1.finisher(); | |
Function<A2, R2> c2Finisher = downstream2.finisher(); | |
class PairBox implements Accumulative<T, PairBox, R> { | |
A1 left = c1Supplier.get(); | |
A2 right = c2Supplier.get(); | |
@Override | |
public void accumulate(T t) { | |
c1Accumulator.accept(left, t); | |
c2Accumulator.accept(right, t); | |
} | |
@Override | |
public PairBox combine(PairBox other) { | |
left = c1Combiner.apply(left, other.left); | |
right = c2Combiner.apply(right, other.right); | |
return this; | |
} | |
@Override | |
public R finish() { | |
R1 r1 = c1Finisher.apply(left); | |
R2 r2 = c2Finisher.apply(right); | |
return merger.apply(r1, r2); | |
} | |
} | |
return Collector.of(PairBox::new); | |
} | |
} |
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
class CoverageContainer implements Accumulative<IssueWiseText, CoverageContainer, Double> { | |
int totalIssueLength = 0; | |
int totalTextLength = 0; | |
@Override | |
public void accumulate(IssueWiseText t) { | |
totalIssueLength += t.issueLength(); | |
totalTextLength += t.textLength(); | |
} | |
@Override | |
public CoverageContainer combine(CoverageContainer other) { | |
totalIssueLength += other.totalIssueLength; | |
totalTextLength += other.totalTextLength; | |
return this; | |
} | |
@Override | |
public Double finish() { | |
return (double) totalIssueLength / totalTextLength; | |
} | |
} |
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
public interface IssueWiseText { | |
int textLength(); | |
int issueLength(); | |
} |
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
class Usage { | |
static Collector<IssueWiseText, ?, Double> toIssueCoverage() { | |
return Collector.of(CoverageContainer::new, Collector.Characteristics.UNORDERED); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment