(a) -> { return a * a; }
(a, b) -> { return a + b; }
() -> { return true; }
Το πακέτο java.util.function
ορίζει μια σειρά
από διεπαφές FunctionalInterface
:
Function
: T → RPredicate
: T → booleanConsumer
: T → voidSupplier
: () → RUnaryOperator
: T → TBinaryOperator
: T, T → T
Οι παραπάνω μπορούν κατά περίπτωση να εξειδικευτούν παραπάνω με ένα από τα
προθέματα:
Bi
(δέχεται δύο ορίσματα)·
Int
,
Long
,
Double
(τύπος ορίσματος)·
Το
(τύπος επιστροφής).
Function compose(Function g)
Όταν εφαμοστεί σε μια συνάρτηση f, επιστρέφει μια νέα συνάρτηση
που είναι η εφαρμογή της f στο αποτέλεσμα της g.
R apply(T t)
Εφαρμογή συνάρτησης σε τιμή.
void accept(T t)
Κατανάλωση της τιμής τT get()
Παραγωγή τιμήςimport java.util.function.Function;
class Lambda {
public static void main(String args[]) {
// Assign lambda to variable
Function<Integer, Integer> square = (a) -> a * a;
// Apply function to value
System.out.println(square.apply(2));
// Pass function to method and obtain function result
Function<Integer, Integer> fourthPower = square.compose(square);
System.out.println(fourthPower.apply(2));
}
}
::
.
import java.util.function.UnaryOperator;
import java.math.BigInteger;
// Methods compatible with a functional interface
class FunctionalFactorial {
public static BigInteger factorial(BigInteger i) {
if (i.equals(BigInteger.ZERO))
return BigInteger.ONE;
else
return i.multiply(factorial(i.subtract(BigInteger.ONE)));
}
public BigInteger instanceFactorial(BigInteger n) {
return factorial(n);
}
// Prints 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
public static void main(String args[]) {
UnaryOperator<BigInteger> f;
f = FunctionalFactorial::factorial;
System.out.println(f.apply(new BigInteger("100")));
f = new FunctionalFactorial()::instanceFactorial;
System.out.println(f.apply(new BigInteger("100")));
}
}
import java.util.function.DoubleUnaryOperator;
import java.util.function.Function;
import static java.lang.Math.abs;
import static java.lang.Math.PI;
class Inverse {
public static void main(String args[]) {
Function<DoubleUnaryOperator, DoubleUnaryOperator> inverse =
f -> x -> 1. / f.applyAsDouble(x);
DoubleUnaryOperator cot = inverse.apply(Math::tan); // συνεφαπτομένη
DoubleUnaryOperator sec = inverse.apply(Math::cos); // τέμνουσα
DoubleUnaryOperator csc = inverse.apply(Math::sin); // συντέμνουσα
final double EPSILON = 1e-15;
assert abs(sec.applyAsDouble(0) - 1) < EPSILON;
assert abs(csc.applyAsDouble(PI / 2) - 1) < EPSILON;
assert abs(cot.applyAsDouble(PI / 4) - 1) < EPSILON;
}
}
Μια ροή (stream) είναι μια επεξεργάσιμη ακολουθία ενός απροσδιόριστου αριθμού στοιχείων. Σε σχέση με μια συλλογή η ακολουθία έχει τις παρακάτω ιδιότητες.
Stream
IntStream
LongStream
DoubleStream
Η παραμετρική κλάση java.util.Optional
περιέχει μια τιμή
τύπου T που μπορεί και να απουσιάζει.
static empty()
Επιστρέφει ένα άδειο αντικείμενο.isPresent()
Επιστρέφει αληθές αν υπάρχει τιμή.get()
Επιστρέφει την τιμή, αν υπάρχει.Collection
με τη μέθοδο stream()
Stream.of(Object[])
IntStream.range(int, int)
Stream.iterate(Object, UnaryOperator)
BufferedReader.lines()
Random
με τη μέθοδο ints()
κ.α.Stream filter(Predicate predicate)
Stream map(Function mapper)
Stream flatMap(Function mapper)
Stream distinct()
Stream sorted()
Stream peek(Consumer action)
Stream limit(long maxSize)
Stream skip(long n)
Stream takeWhile(Predicate predicate)
Stream dropWhile(Predicate predicate)
void forEach(Consumer action)
Object[] toArray()
T reduce(BinaryOperator accumulator)
R collect(Collector)
(π.χ. Collectors.groupingBy)long count()
boolean anyMatch(Predicate predicate)
boolean allMatch(Predicate predicate)
boolean noneMatch(Predicate predicate)
Optional findFirst()
Optional findAny()
/*
* Output ordered list of a file's unique words
*/
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;
import java.io.IOException;
class UniqueWords {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("Usage: UniqueWords file");
System.exit(1);
}
try {
Files
.lines(Paths.get(args[0]))
.flatMap(line -> Stream.of(line.split("\\W+")))
.sorted()
.distinct()
.filter((x) -> x.length() > 0)
.forEach(System.out::println);
} catch (IOException e) {
System.err.println("Error reading line: " + e.getMessage());
System.exit(1);
}
}
}
/**
* Estimate the number of distinct elements in a data stream
* (F0 estimation problem) based on the algorithm published in the
* Proceedings of 30th Annual European Symposium on Algorithms (ESA 2022)
* https://doi.org/10.48550/arXiv.2301.10191
*/
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class F0Estimator {
/**
* Estimate number of unique elements in the passed stream
* @param storageSize The storage to use
*/
public static <T> long estimateF0(Stream<T> stream, int storageSize) {
final float LOAD_FACTOR = 0.5f;
// Probability to add an element; in an array as a workaround
// for using it in a lammbda expression
final double[] p = {1.0};
Set<T> X = new HashSet<>(storageSize , LOAD_FACTOR);
Random random = new Random();
stream.forEach(element -> {
if (random.nextDouble() < p[0])
X.add(element);
else
X.remove(element);
if (X.size() >= storageSize) {
// Randomly keep each element in X with probability 1/2
X.removeIf(e -> random.nextDouble() < 0.5);
p[0] /= 2;
if (X.size() >= storageSize) {
throw new IllegalStateException("Threshold exceeded after sampling");
}
}
});
return (long) (X.size() / p[0]);
}
public static void main(String[] args) {
// Create a Random instance
Random random = new Random();
// Create a stream of 1e9 random integers with 65536 distinct values
Stream<Integer> stream = IntStream
.generate(random::nextInt).limit(1_000_000_000)
.map(i -> i & 0xffff)
.boxed();
final int STORAGE_SIZE = 1000;
long uniqueCount = estimateF0(stream, STORAGE_SIZE);
System.out.println("Estimated number of unique elements: " + uniqueCount);
}
}
Το παρακάτω παράδειγμα υπολογίζει τη √2 δημιουργώντας μια άπειρου μήκους ακολουθία με διαδοχικά καλύτερες προσεγγίσεις.
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.DoubleFunction;
import java.util.stream.Stream;
/** Find a square root using the Newton-Raphson approximation */
class SquareRoot {
/** Obtain successive approximations of a function's root using the
* Newton-Raphson method. */
static class NewtonRaphson {
/** f(x) and f'(x) */
DoubleFunction fx, fdx;
NewtonRaphson(DoubleFunction fx, DoubleFunction fdx) {
this.fx = fx;
this.fdx = fdx;
}
/** Return next approximation, given the previous one */
Double nextApproximation(double previous) {
// xₙ₊₁ = xₙ - f(xₙ) / f′(xₙ)
return previous - (double)fx.apply(previous) / (double)fdx.apply(previous);
}
}
/** Test whether successive parts of a series differ more than a value */
static class NotWithin implements Predicate<Double> {
/** Previous value in series */
Optional<Double> previous = Optional.empty();
/** Difference value above which the test method returns true */
Double epsilon;
NotWithin(double d) {
epsilon = d;
}
/**
* Return true if successive parts of the series do not differ by
* less than the specified epsilon.
*/
@Override
public boolean test(Double d) {
boolean r;
if (previous.isPresent())
r = (Math.abs(previous.get() - d) > epsilon);
else
r = true;
previous = Optional.of(d);
return r;
}
}
public static void main(String args[]) {
final double SQRT_TO_FIND = 2;
DoubleFunction fx = (x -> x * x - SQRT_TO_FIND); // f(x) = x² - α
DoubleFunction fdx = (x -> 2 * x); // f'(x) = 2x
var rootTwo = new NewtonRaphson(fx, fdx);
var greaterThanEpsilon = new NotWithin(1e-15);
// SQRT_TO_FIND is also our first approximation
System.out.println(Stream.iterate(SQRT_TO_FIND, rootTwo::nextApproximation)
.dropWhile(greaterThanEpsilon)
.findFirst()
.get());
}
}