Mitosis and Meiosis
Skim these notes to review the main points quickly.
This set of Java programming language study notes covers three modern features central to writing clean, functional-style code: lambda expressions, method references, and the Stream API. You will learn the syntax and rules for each feature, the built-in functional interfaces Java provides, and how Streams process data through pipelines of intermediate and terminal operations. The examples are drawn directly from Java 8 and later.
Lambda expressions were introduced in Java 8 to make code shorter, cleaner, and easier to read. They are a concise way to represent an anonymous function, essentially a method without a name, used when you want to pass code as a value.
Lambda expressions are used to write less code, avoid creating unnecessary classes, pass behavior (code) as a parameter, and work easily with Collections and Streams.
Lambda expressions can only be used when you have a Functional Interface. A Functional Interface is defined as an interface with exactly one abstract method.
The basic syntax of a lambda expression is (parameters) -> { body }. The parameters are the inputs to the expression, the arrow separates parameters from the body, and the body contains the code to be executed.
Lambda expressions can have zero parameters (e.g., () -> System.out.println("Hello")), one parameter (e.g., p -> System.out.println(p)), or multiple parameters (e.g., (a, b) -> a + b).
Lambda expressions are particularly useful when working with Java Collections and Streams. They allow for concise operations like filtering, mapping, and iterating over collections.
Java provides several built-in functional interfaces, including Predicate (tests a condition), Consumer (accepts input and performs an action), Supplier (provides output), and Comparator (compares objects).
A method reference is a shortcut for a lambda expression that calls an existing method. It's used when your lambda expression's sole purpose is to invoke a pre-defined method.
There are four types of method references: Static Method Reference (ClassName::method), Instance Method Reference of a Particular Object (obj::method), Instance Method Reference of an Arbitrary Object of a Type (ClassName::method), and Constructor Reference (ClassName::new).
A static method reference is used when the method being referenced is static. The syntax is ClassName::staticMethod. For example, Collections.sort(personList, Geeks::compareByName) if compareByName is a static method in the Geeks class.
This type is used when you want to call a method on a specific, already existing object. The syntax is object::method. For example, cmp::compareByName, where cmp is an object with a compareByName method.
This reference is used when you want to call a method on any object of a class, not a specific one. The syntax is ClassName::method. An example is String::compareToIgnoreCase, used for sorting strings.
A constructor reference is used to create new objects. The syntax is ClassName::new. For instance, Person::new can be used to create new Person objects, often within stream operations.
A Stream in Java is a sequence of elements that can be processed in a functional style. Streams are lazy, meaning intermediate operations don't execute until a terminal operation is called, and they are single-use; once consumed, they cannot be reused.
Streams can be created from various sources, including Collections (list.stream()), Arrays (Arrays.stream(arr)), specific values (Stream.of(1, 2, 3)), and infinite sequences (Stream.iterate(...).limit(...)).
A stream pipeline consists of a Source, Intermediate Operations, and a Terminal Operation. The source provides the data, intermediate operations transform the stream (e.g., filter, map), and the terminal operation produces a result (e.g., forEach, collect).
Key intermediate operations include filter(p) to keep matching elements, map(f) to transform elements, distinct() to remove duplicates, sorted() to sort elements, and limit(n)/skip(n) to control the number of elements.
Important terminal operations are forEach(consumer) to perform an action on each element, collect(toList()) to gather elements into a collection, count() to get the number of elements, reduce(...) to combine elements, and findFirst()/findAny() to retrieve an element.
Streams can be sequential (default, single-threaded), parallel (multi-threaded for potential performance gains), infinite (requiring a limit), or primitive streams (IntStream, LongStream, DoubleStream) for efficient primitive type processing.
A Collection stores data (like List or Set), while a Stream processes data. Streams are pipelines, not storage mechanisms, and are typically derived from collections.
Java Streams can be used to process files efficiently. Using try-with-resources ensures that file streams are automatically closed. For example, reading lines from a file and filtering them.
Remember that streams are single-use. Intermediate operations are lazy. Avoid side-effects within stream operations, especially with parallel streams, and prefer collect() for thread-safe accumulation.
Skim these notes to review the main points quickly.
Skim these notes to review the main points quickly.
Skim these notes to review the main points quickly.
Skim these notes to review the main points quickly.
Turn your own material into organized study notes you can edit and review.