An online markdown blog and knowledge repository.
This page is a collection of notes taken while working through Oracle Java Tutorials and supplemental information made public by Baeldung.com.
Library: java.util.stream
Extends: BaseStream<T,Stream<T>>
"A sequence of elements supporting sequenctial and parallel aggregate operations."
Example aggregate operation using Stream
and IntStream
[docs.oracle.com]:
int sum = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(w -> w.getWeight())
.sum();
IntStream
is one of several 'primitive specializations' (Streams).
A Stream represents a pipeline.
Stream Pipeline sources could be Collections, Arrays, a Generator Function, I-O Channel, etc.
Stream Pipelines include a processing section for 'intermediate operations'.
The end of a Stream Pipeline is denoted by a 'terminal operation', producing a result or side effect.
Streams are lazy, meaning they do not operate until the 'terminal operation' is initiated.
Source Elements are consumed on an as-needed basis.
Goals:
.iterator()
and .spliterator()
methods to control Stream traversal, more like a collection.Think of Stream Operations "as a query on the stream source." Could be similar to DotNET LINQ Operators.
Operations that alter the Source could cause unexpected Stream Pipeline behavior.
Use lambda expressions w -> w.getWeight()
to specify behavior.
Necessary behavioral parameters:
Streams Rules:
IllegalStateException
when Stream reuse is detected.BaseStream.close()
: Streams with an IO-Channel source will require closing. Collection-sourced Streams do not. Use 'try-with-resources' to auto-close.Streams can be made to be:
BaseStream.sequential()
or Collection.stream()
.BaseStream.parallel()
or Collection.parallelStream()
.BaseStream.isParallel()
.Static Interface Stream.Builder<T>
can be used as a mutable builder for a Stream.
8-bit data streams.
Byte Stream classes are decendants of InputStream and OutputStream.
Seems like its a good idea to import java.io.IOException
for these streams.
Instantiate FileInputStream with the filename of the data source.
Instantiate FileOutputStream with a write-to filename.
// always initialize Streams as null
FileInputStream in = null;
FileOutputStream out = null;
// set up the input and output files
in = new FileInputStream("input_file.txt");
out = new FileOutputStream("output_file.txt");
Use a loop to read-in data until FileInputStream sees byte -1
.
try {
int character;
while((character = in.read()) != -1) {
out.write(character);
}
}
Always close streams - use a Finally block to safely close them:
finally {
// if a file was not opened, in will be null and never opened
if (in != null) {
in.close();
}
// same here, no file, out will be null and should not be closed
if (out != null) {
out.close();
}
}
Java Basic IO Character Streams.
Reader
and Writer
.CopyCharacters class example [Docs.Oracle.com]:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyCharacters {
public static void main(String[] args) throws IOExecption {
FileReader inputStream = null; // Character Stream uses FileReader for input
FileWriter outputStream = null; // Character Stream uses FileWriter for output
try {
inputStream = new FileReader("input_file.txt");
outputStream = new FileWriter("character_output_file.txt");
int character; // holds Character VALUE
while ((character = inputStream.read()) != -1) {
outputStream.write(character);
}
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
}
This sub-section is largely a copy from [Docs.Oracle.com]
\r\n
are multi-character encodings.BufferedReader
and PrintWriter
to support Line-Oriented IO (more on these later).String
rather than an int, processing data line-by-line e.g. .readLine()
.Note: Line terminators might be altered by PrintWriter from whatever the source terminator(s), to terminator(s) native to the current environment or Operating System.
Scanning and Formatting have more capability in regards to longer data read and write operations, and is covered in a later section.
Unbuffered Streams:
Buffered IO Streams:
var inputStream = new BufferedReader(new FileReader("input_file.txt"));
var outputSTream = new BufferedWriter(new FileWriter("output_file.txt"));
Four buffered stream classes:
println
or format
are called.flush()
method. Has no effect on unbuffered Stream types.Usually requires translating between human-friendly and computer-ready data strams.
Character.isWhitespace
)..useDelimiter(",\\s*");
Scanner:
27,000
is an integer.Basic Implementation of Scanner:
Scanner scanner = null
try-finally
block and ensure Scanner instance is closed before exiting the program.while (scanner.hasNext()))
to step through the delimited data tokens.PrintWriter (Character Stream class) and PrintStream (Byte Stream class) support formatting.
PrintWriter supports formatting an output stream for human use.
PrintStream is better for System.out
and System.err
data stream formatting.
Formatting Levels:
print
and println
: Format individual values.format
is similar to Format String and has many formatting options.Use System.out.format
with %s
value placeholders, eol tokens, etc.
%
: Value placeholder.s
: Conversion type String.f
: Floating point.n
: New line.x
: Hexadecimal.tB
: Integer to locale-specific month name.Note: %n
will always produce the correct New Line code for the local OS locale but \n
will not!
Format Specifier Elements:
%
1$
to identify specific argument to place, otherwise in order left to right
+0
formatting options such as preceeding '+' sign, the padding character e.g. '0', locale-specific thousands separator e.g. ',' depends on selected conversion
20
minimum width of formatted value, left padded
.10
decimal places (for f) or total character length (for s), right truncated
f
floating pointJava handles this via 2 means:
Standard Streams:
Standard IOs are defined automatically in Java:
To use Standard Input as a character stream wrap System.in
in InputStreamReader
.
readPassword
method.reader
and writer
methods to get input and output Character Streams from/to it.There is a basic example of how to implement a password changing program using the Conole by Oracle Docs.
Class DataOutputStream can only be instantiated as a wrapper to an existing ByteStream object, and provides buffers. Same for DataInputSTream.
var out = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(input_file)
)
);
DataOutputStream methods include:
.writeDouble(double_value)
.writeInt(int_value)
.writeUTF(String_value)
-> Variable-width character encoding consumes a single byte for "common Western characters".For both Input and Output the instantiation pattern is: File Stream(file_name), wrapped by Buffered Stream, wrapped by Data Stream.
Note: DataInputStream throws EndOfFileException
when it finds the end of a file, rather than setting a specific state.
Note: Data Streams use Floating-point values to represent monetary Numbers, so certain decimals will not convert properly using Data Streams!
Serializable
.Object Stream Classes are:
Object Stream Classes implement subinterfaces ObjectInput and ObjectOutput of DataInput and DataOutput, respectively.
Benefit: Object Streams support BigDecimal
objects for fractional value representations (better than floating-point as in Data Streams).
Reconstituting an object from a stream can be tricky.
Oracle Java Docs refers to JDK 8 release and java.nio.file package and its sub-packages in this sub-section.
The documentation reviews paths, both relative and absolute, and Symbolic Links.
Unix-based paths are not comparable to Windows-based paths due to their native naming convention. However, both can be handled and processed by Java.
Symbolic Links are:
Java handles circularly-referenced Symlinks.
Create a path using the Paths helper class:
Path path1 = Paths.get("/home");
Path path2 = Paths.get(args[0]);
Path path3 = Paths.get(URI.create("file:///Users/user/file.java"));
Paths helper is shorthand for FileSystems.getDefault().getPath(String);
Notes (source java nio File Paths helper class):
Path()
public static Path get(String first, String...)
:right-arrow: Best used with Path.resolve helper e.g. Path dir=""; Path path=dir.resolve("file");
public static Path get(URI uri)
:right-arrow: Iterates over installed providers to find one that handles the URI scheme.Create a file and get a reference to it in either Unix or Windows:
Path myFile = Paths.get(System.getProperty("user.home"), "logs", "logfile.txt");
Path syntax can be bound to the underlying OS sematics but it might be better to use Environment Variables and Path instance methods to get info:
Path path = Paths.get("/home/username/log-file.log");
path.toString(); // /home/username/log-file.log
path.getFileName(); // log-file
path.getName(0); // /home
path.getNameCount(); // count of elements in path: 3
path.subpath(0,2); // /home/username
path.getParent(); // /home/username
path.getRoot(); // The root path: /
path.normalize(); // removes redundant indicators like . and .. from the path
Key takeaway: Path
class contains methods for manipulating a path.
Three methods to do so:
Path path1 = Paths.get("/home/users");
path.toAbsolutePath(path1); // /home/users
Path userInputPath = Paths.get(args[0]);
path.toAbsolutPath(userINputPath); // /root/pathOfExecutable/userInputPath
toRealPath()
: Returns the real path of an existing file:
Note: Leverage NoSuchFileException to catch errors related to Path and Paths operations.
Join Paths using Paths.get("path_one").resolve("child_path")
.
Use Relativize method to return the path to a sibling path from a starting path:
Path p1 = Paths.get("foo");
Path p2 = Paths.get("bar");
Path p1_to_p2 = p1.relativize(p2); // ../bar
Path p2_to_p1 = p2.relativize(p1); // ../joe
// use this to avoid typing stuff like Paths.get("../../grandParentPath/baz")
Note: Relative paths might be a problem for Relativize depending on the root path and the OS.
Compare Two Paths using equals:
path.equals(otherPath); // returns boolean if path equals otherPath
path.startsWith(beginning); // returns boolean of Path root is equal to "beginning"
path.endsWith(ending); // returns boolean of Path last-child is equal to "ending"
Path implements:
compareTo()
and isSameFile()
(two paths locate the same file) are exposed.Files
class is a java.nio.file
package entrypoint.
Path
objects.Terminology:
java.io.Closeable
interface. This is used to limit memory leaks. Use-with-resources is a common way to do this. Another is to call an Object's close()
method before leaving an execution scope.IOException
is common. Use .close()
method within a Finally block if the object is null.Path Files.move(Path, Path, CopyOption...)
where ...
means pass-in a comma-separated list of values. CopyOption[]
would mean to pass-in an array of type CopyOption instances.move()
method is atomic meaning all-or-nothing operation. Success or failure, no in-between.String value = Charset.defaultCharset().decode(buf).toString();
.Key Takeaways on Files class methods:
Glob syntax rules:
{temp*, tmp*}
.[abcdefg]
or [0-9]
.* ? \
match themselves within brackets).\
. Use \\
to match a single \
character.Note: Command-line escape characters might differ between systems.
Consider Globs to be like RegEx syntax that java.nio.file
understands.
Verify existence!
exists(Path, LinkOption...)
notExists(Path, LinkOption...)
Three possible results:
File Accessibility:
isReadable(Path)
: BooleanisWriteable(Path)
: BooleanisExecutable(Path)
: BooleanNote: TOCTTOU (Tock-too) errors can occur when testing for Read, Write, and Executability and then accessing the file. Read about how to avoid this situation.
Two Paths Can Locate The Same File!
isSameFile(Path, Path)
: BooleanFiles and Directories:
Symlinks:
Deletion Methods:
delete(Path)
: Succeeds or throws an Exception.deleteIfExists(Path)
: Use when multiple threads could be deleting same path/item. Silently fails.copy(Path, Path, CopyOption...)
REPLACE_EXISTING
NOT specified.CopyOption Enums/Varargs:
DirectoryNotEmptyException
.Example:
import static java.nio.file.StandardCopyOption.*;
Files.copy(source, target, REPLACE_EXISTING);
Note: Files.walkFileTree()
method supports recursive copying.
move(Path, Path, CopyOption...)
: Same failure mode as Copying.
CopyOption Enums/Varargs:
Example:
import static java.nio.file.StandardCopyOption.*;
Files.move(source, target, REPLACE_EXISTING);
Filesystem metadata is stored IN files and directories.
AKA File Attributes.
See Oracle Java Docs File Attributes for methods that act of Filesystem Metadata.
Metadata is collected into Views so that effectively similar information can be displayed, whether POSIX or DOS style Filesystem:
BasicFileAttributeView
DosFileAttributeView
PosixFileAttributeView
FileOwnerAttributeView
AclFileAttributeView
UserDefinedFileAttributeView
Note: Not all views are supported by all systems, and some file systems support views NOT supported in java.nio.file libraries.
FileAttributeView interfaces can be accessed via getFileAttributeView(Path, Class<V>, LinkOption...)
but in most cases is not necessary.
Some FileAttributes can be programmatically set. See Oracle Java Docs File Attributes for methods and usage.
Utility Methods for simpler, common cases:
Files.write(file, buf)
Iteration over a stream or lines of text and interop with java.io package (which includes Buffered Streaming capability):
More complex, less common methods:
Locking and memory-mapped IO required:
OpenOptions Parameter:
Reading File with Stream IO:
newInputStream(Path, OpenOption...)
: Returns unbuffered input stream for oreading bytes from a file. Wrap an instance of InputStream
with a BufferedReader
instance.Creating, Writing File with Stream IO:
newOutputStream(Path, OpenOption...)
: Returns an unbuffered output stream. Utilize OpenOption Enums to flag specific behavior e.g. CREATE, APPEND
, etc.Two methods for reading and writing channel IO:
newByteChannel(Path, OpenOption...)
newByteChannel(Path, Set<? extends OpenOption>, FileAttribute<?>...)
These return an instance of a SeekableByteChannel
that can be cast to a FileChannel
which can allow mapping file contents to memory for faster access.
Key Takeaway: To seek to a specific location in a file and read it, use Files.newByteChannel
and its returned instance of SeekableByteChannel
to read/write from/to any position within a file.
See Oracle Docs File for more (there is a lot).
createFile
is an atomic operation method.
There example code of how to create a file with default attributes using createFile().
For temporary files, use:
createTempFile(Path, String, String, FileAttribute<?>)
: Specify a temporary file location.createTempFile(String, String, FileAttribute<?>)
: Use File-system Default temp location.See Oracle Docs File for an example.
Non-sequential access to file contents.
SeekableByteChannel interface provides a Channel I-O.
SeekableByteChannel API Methods:
position
: Returns current position.position(log)
: Sets current position.read(ByteBuffer)
: Reads bytes into buffer.write(ByteBuffer)
: Writes bytes from buffer.truncate(long)
: Truncates a file (or entity) connected to the channel.Path.newByteChannel
methods return an instance of SeekableByteChannel.
SeekableByteChannel can be cast to FileChannel
providing advanced features: Mapping a region to memory, locking a region of a file, and reading/writing bytes from an absolute position.
There is a code snippet that demonstrates the use of ByteBuffer
, FileChannel
, position
, rewind
, nread
, and write
.
FileSystem
Class contains a variety of methods for obtaining information about the file system.
List Directories:
FileSystem.getRootDirectories()
: Implements Iterable interface.getDefault().getRootDirectories()
for directories in Root.Create a Directory:
Files.createDirectory(Path, FileAttribute<?>)
: Without FileAttribute, a default set of attirbuites will be applied."rwxr-x---"
e.g. Set<PosixFilePermissions> perms = PosixFilePermissions.fromString("rwxr-x---");
Create a Temporary Directory:
createTempDirectory(Path, String, FileAttribute<?>...)
: Allows code to specify a location for the temp directory.createTempDirectory(String, FileAttribute<?>...)
: Creates a new directory in the default temporary-file directory.Listing Directory Contents:
newDirectoryStream(Path)
: Implements DirectoryStream interface, which implements Iterable.DirectoryStream<Path> stream = Files.newDirectoryStream(dir))...
Be sure to use Try-with-resources because the returned DirectoryStream is a Stream.
Everything including files, links, subdirectories, and hidden files will all be returned unless Globbing is used:
DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.{java,class,jar}"))
etc.
Use DirectoryStream.Filter<T>
interface, method accept()
:
Create Filter partial example: DirecotryStream.Filter<Path> filter = newDirectoryStream.Filter<Path>() { public boolean accept(Path file) throws IOException { ... }}
Invoke custom Filter partial example: try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) { ... }
See Oracle's Java Documentation on Writing Your Own Directory Filter for the full example.
java.nio.file
package supports links and behavior can be configured when a Symlink is discovered.
Notes about Hard Links:
Create a Symbolic Link:
createSymbolicLink(Path, Path, FileAttribute<?>)
: Path1 = newLink; Path2 = target.Create a Hard Link:
createLink(Path, Path)
Detecting a Symlink:
isSymbolicLink(Path)
Finding Link Target:
readSymbolicLink(Path)
Key Takeaway: Detect Symlinks by using java.nio.file
package File
instanace .isSymbolicLink(Path)
for a boolean result.
FileVisitor Interface:
FileVisitor
to detmine behavior through traversal process.FileVisitor Traversal Behavior Methods:
preVisitDirecotry
: Invoked before directory entries are visited.postVisitDirectory
: Invoked after all entries in directory are visited. Exceptions are passed to this method.visitFile
: Invoked on the file being visited and returns BasicFileAttributes to the method. FileAttributes package can be used to read specific attributes.visitFileFailed
: Invoked when the file cannot be accessed and an Exception is passed to the method for handling (throw it, log it, etc).Review FileVisitor Interface documentation on Oracle JavaSE Docs for examples and more info.
Initiating a Traversal with these Methods and options enum:
walkFileTree(Path, FileVisitor)
: Path is starting point, FileVisitor is your FileVisitor instance.walkFileTree(Path, Set<FileVisitOption>, int, FileVisitor)
: Enables specifying limit on number of levels to visit via FileVisitOptions
enums.FOLLOW_LINKS
: A FileVisitOptions enum that enables following Symlinks.FileVisitor Considerations:
preVisitDirectory
, visitFiles
, and postVisitDirectory
visitFile
to perform a file search does not find Directories. To get direcotories, perform a comparison in either preVisitDirectory
or postVisitDirectory
methods.walkFileTree
does not follow Symlinks.visitFile
method is invoked for files. If configured to follow Symlinks, recursive/loop discoveries will be reported in visitFileFailed
method with FileSystemLoopException
Controlling the Flow of FileVisitor:
FileVisitor methods return a FileVisitResult
value where you control the flow of the traversal:
preVisitDirectory
method.preVisitDirectory
method returns this value.preVisitDirectory
method, postVisitDirectory
method is not invoked and neither the specified directory nor its siblings will be visited.Check out Controlling the Flow Code Snippets and Examples at page bottom at Oracle JavaSE Docs.
Use pattern matching to help, i.e. *
for any, ?
for single placeholder, etc.
File System implementations in java.nio.file
provides a PathMatcher
functionality.
PathMatcher accepts Glob or Regular Expressions as the the params to .getPathMatcher()
.
Steps to use:
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.{java, class});`
Path filename = ...;
if (matcher.matches(filename)) {
System.out.println(filename);
}
Example java taken directly from the Finding Files section of [javase tutorials on essential io]
Recursive pattern matching is used to find a file somewhere within a file system.
Use Find
with a glob pattern to search the file system for a file.
The Find class:
SimpleFileVisitor<Path>
PathMatcher
as a field and the input args are used to set up the pattern
for pattern matching.@Overrides
that define preVisitDirectory()
, visitFileFialed()
, and visitFile()
(all FileVisitor traversal behavior methods).walkFileTree()
(Find is a simple FileVisitor polymorphism).count
for the number of matching files found while walking the file tree.Utilize functionality called 'file change notification'.
Detection through file system changes is inefficient, so java.nio.file
has an API for that:
There is also an OVERFLOW event, but registration is not required in order to receive it (probably an unchecked exception escape hatch).
Oracle recommends caution when deciding to implement their 'Watch Service API' referenced in the documentation:
Use probeContentType(Path)
to get a file's MIME Type:
try {
String type = Files.probeContentType(filename);
if(type==null) {
System.err.format("%s has an unknown filetype.%n", filename);
} else if (!type.equals("text/plain")) {
System.err.format("%s is not a plain text file.%n", filename);
continue;
}
catch (IOException ioex) {
System.err.println(ioex);
}
}
Return is either String or null (if cannot be determined).
Content Type is generally managed by the platform's type detector, so presumptive content-types like: Extension .class
is a Java file, might not be correct.
Take a look at FileTypeDetector in the Oracle Java Docs. Note that it still 'guesses'.
Use FileSystems.getDefault()
method and chain it with FileSystem
type methods. Example:
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.*");
Path separators are not always the same:
\
/
Retreive the path separator character using:
String separator = File.separator;
orString separator = FileSystems.getDefault().getSeparator();
Files and Directories are actually stored within a File System's Stores.
File Store
represents the underlying storage device:
List all file stores the system has:
for (FileStore store: FileSystems.getDefault().getFileStores()) {
...
}
Get a specific File Store:
Path file = ...;
FileStore store = Files.getFileStore(file);
Prior to Java SE 7:
java.io.File
was used.java.io.File.toPath()
converts File instance to java.nio.file.Path
instance: Path input = file.toPath();
There is a large matrix of functionality that relates java.io.File
functionality to java.nio.file
Functionality, along with links to tutorials on usage at Oracles Java Docs on File IO.
What Class and method would you use to read a few pieces of data that are at known positions near the end of a large file?
Use
Files.newByteChannel
to get instance ofSeekableByteChannel
which allows reading from/writing to any position in a file.
When invoking format
, what is the best way to indicate a new line?
Use
%n
which is platform independant.
How would you determine the MIME type of a file?
Two options: Use
Files.probeContentTypes(Path: file_name)
(which uses the file systems underlying file type detector). Note: Oracle suggested an optionalFileTypeDetector
code file to try.
What method(s) would you use to determine whether a file is s symbolic link?
Use
Files.isSymbolicLink(Path: file_name)
method.
Note:
Paths
class method .toRealPath(Path: symlink_name)
to get the actual path to a Symlinked file.Stream.empty()
to create a new Stream and test whether it is empty before tryingn to use it.Collection<Integer> numbersStream = Arrays.asList(1, 2, 3); Stream<Integer> streamOfNumbers = numbersStream.stream();
Stream<Integer> streamOfArray = Stream.of(1, 2, 3);
Stream.builder()
with a type hint and a size (otherwise is 'infinite'): Stream<Integer> generatedStream = Stream.Generate(()->1).limit(5);
Stream<Integer> iterStream = Stream.iterate(1, num-> num * 2).limit(4);
1, 2, 4, 8
IntStream
, LongStream
, and DoubleStream
.chars()
method, which produces Integer representations of characters (example below).lines()
method, Java.NIO.Files
class supports streaming file data (example below).Examples:
// Stream of String (of Integer representation of Characters)
IntStream streamOfChars = "word".chars();
// use RegEx
Stream<String> streamOfString = Pattern.compile(", ").splitAsStream("X", "Y", "Z");
// Stream of File (of Strings)
Path path = Paths.get('C:\\example.txt');
Stream<String> streamOfStrings = Files.lines(path);
Stream<String> streamWithCharset = Files.lines(path, Charset.forName('UTF-8'));
[Java 8 Streams at Baeldung.com]
stream.findAny()
is an example of a method that will work but an IllegateStateException will be thrown (a RuntimeException). This is tricky because it will not be caught at design or compile time
.List<T>
and the collect(Collectors.toList())
nested methods.List<String> elements = Stream.of("X", "Y", "Z")
.filter(element -> element
.contains("Y"))
.collect(Collectors.toList());
Optional<String> anyElement = elements.stream().findAny();
Option<String> firstElement = elements.stream().findFirst();
Sequence of operations for using Streams:
skip()
, map()
, and others.List<String> list = Arrays.asList("alpha", "bravo", "charlie");
long size = list.stream().skip(1).map(item -> item.substring(0, 3)).sorted().count()
TODO:
map()
) force the Intermediate operation(s) to execute and produce results.skip()
, filter()
, and distinct()
at the top of the Stream pipeline.count()
, max()
, min()
, and sum()
.reduce()
and collect()
.Variations:
OptionalInt reduced = IntStream.range(1, 4).reduce((a, b) -> a + b);
// 1+2, 3+3 returns 6
int reducedTwoparams = intStream.range(1, 4).reduce(10, (a, b) -> a + b);
// 10 + 1 + 2, 13 + 3 returns 16
int reducedParams = Stream.of(1, 2, 3)
.reduce(10, (a, b) -> a + b, (a, b) -> {
log.info("combiner was called");
return a + b;
});
// combiner will not be called in non-parallel reducers
// however result is still 16
int reducedParallel = Arrays.asList(1, 2, 3)
.parallelStream()
.reduce(10, (a, b) -> a + b, (a, b) -> {
log.info("combiner was called");
return a + b;
});
// combiner called twice and result is 36
// each combiner processing happens in parallel
// 10 + 1 and 10 + 2 and 10 + 3
// so when accumulated is 11, 12, and 13
// and when combined is 11 + 12 + 13 result 36
[Java 8 Streams at Baeldung.com]
Steps:
List<T>
and init as Arrays.asList(new {type(value)}, new {type(vlaue)})
to init or add elements of 'type' into a List..stream().map(MyType::getName).collect(Collectors.toList());
..collect(Collectors.command(args))
.Tools:
.stream().map(MyType::getName).collect(Collectors.joining(", ", "[", "]"));
(args: delimiter, prefix, suffix)..stream().collect(Collectors.averagingInt(MyType::getValue));
(getValue is getter method on MyType)..stream().collect(Collectors.summingInt(MyType::getValue));
(getValue is getter on MyType)..stream().collect(Collectors.summarizingInt(MyType::getValue));
. Follow with toString()
for output..stream().collect(Collectors.groupingBy(MyType::getValue));
.stream().collect(Collectors.partitioningBy(item -> item.getValue > minValue));
.stream().collect(Collectors.collectingAndThen(Collectors.toSet(), Collection::unmodifiableSet));
which will convert Stream to Set, then Set to immutable Set.Custom Collectors can be created using of()
method of type Collector
:
Collector<MyType , ?, LinkedList<MyType>> toLinkedList = Collector.of(LinkedList::new, LinkedList::add, (first, second) -> {
first.addAll(second);
return first;
});
LinkedList<MyType> linkedListOfMythings = listOfMyThings.stream().collect(toLinkedList);
// Collector instance gets reduced to the LinkedList, first element?
Java 8 makes things simpler with:
Create parallel Streams when source is a Collection or an Array type, using parallelStream()
method:
Stream<MyType> streamOfCollection = myTypes.parallelStream();
boolean isParallel = streamOfCollection.isParallel();
boolean bigInstance = streamOfCollection
.map(myTypeItem -> myTypeItem.getValue() * someValue)
.anyMatch(thisValue -> thisValue > 200);
Create parallel Streams using parallel()
when source is not a Collection or an Array type:
IntStream intStreamParallel = IntStream.range(1, 100).parallel();
boolean isParallel = intStreamParallel.isParallel();
Notes:
Convert Parallel Mode Stream back to sequential mode using sequential()
:
IntStream intStreamSequential = intStreamParallel.sequential();
boolean isParallel = intStreamSequential.isParallel();
[Java 8 Streams at Baeldung.com]
Always apply close()
to terminal operations to avoid memory leaks.
Unconsumed Streams will create memory leaks.
See Baeldung Streams FindAny and FindFirst
Find any element within a Stream.
Optional
instance which is empty if the Stream is empty.anyOf(is(a), is(b), ...)
to allow for empty returns mixed with truthy returns.Find the first element in a Stream.
Optional
which is empty if the Stream is empty.See Baeldung Java8 Streams Functional Interfaces
Baeldung Lambda Expressions and Functional Interfaces Tips and Best Practices.
@
annotation.Note: Default Methods do not count as Abstract.
Simple description: Interface with single method takes single value and returns single value public interface Function<T, R> { ... }
.
Example of Standard Library Function Type: Map.computeIfAbsent()
// for a HashMap<String, Integer> myHashMap...
myHashMap.computeIfAbsent(string, (item) -> item.length);
// computeIfAbsent looks for key String and if not found inserts String into map with value item.length
// another way to look at it:
myHashMap.computeIfAbsent(string key, function( (string item) -> return new int(item.length)) );
// breaking it down to a simple implementation:
string foundValueOrNull = computeIfAbsent(string, {calculateValue(arg)});
Converting to a functional interface:
myHashMap.computeIfAbsent(string, String::length);
// consider the pair of colons to imply this is a lambda function
Function Interface has default compose()
method.
How to:
::
.toString()
method.Function2.compose(Function1)
.Very abstract. Here's [The example from Baeldung.com] with comments added by me:
// function uses Functional Interface and stores an Integer, returns a String
Function<Integer, String> intToString = Object::toString;
// function uses lambda and stores String input, returns a String output
Function<String, String> quote = s -> "'" + s + "'";
// function calls intToString, passing in quote and executes its built-in compose() method
Function<Integer, String> quoteIntToString = quote.compose(intToString);
// using the compose in-line to print to the console without creating quoteIntToString
System.out.println(quote.compose(intToString).apply(5));
// prints '5' to the console
Primitive Types cannot be a generic type argument.
Function Interface has ability to use double
, int
, long
, and combinations thereof.
Returning another Primitive Type from a Primitive Function can be achieved using a Functional Interface decorator/attribute, and implementing a custom function.
Example from [Baeldung.com] with my comments added:
@FunctionalInterface
public interface ShortToByteFunction {
// a single method in the functional interface takes a single value and returns a single value
byte applyAsByte(short s);
}
// allow passing in a Lambda Expression using ShortToByteFunction interface
public byte[] transformArray(short[] array, ShortToByteFunction function) {
byte[] transformedArray = new byte[array.length];
// step through each element in input array and apply the custom function interface method
for (int i = 0; i < array.length; i++) {
transformedArray[i] = function.applyAsByte(array[i]);
}
return transformedArray;
}
// transform an array of shorts to an array of bytes multiplied by 2
short[] array = {(short) 1, (short) 2, (short) 3};
byte[] transformedArray = transformedArray(array, s -> (byte) (s * 2));
byte[] expectedArray = {(byte) 2, (byte) 4, (byte) 6};
assertArrayEquals(expectedArray, transformedArray);
This is an excellent example of why an Interface is important in object oriented programming. If any function was passed in but did not have the .applyAsByte(short s)
method applied, the code would not compile.
Two-Arity Function Specializations at Baeldung.com
Define lambdas with 2 input args using library functions with "Bi" in the name.
When using a Map function (or Map-like for e.g. HashMap.ReplaceAll()
), a Two-Arity function can allow processing 2 values and returning a single result that the ReplaceAll()
function proceses to elements in the HashMap.
A Function Specialization that takes zero arguments.
Commonly used for lazy-generation or values.
Returns a Supplier type that can then be treated as a Stream object, and the 'state' values are treated as final.
Available Suppliers:
A 'void' type Function that accepts Generic Inputs.
Useful for generating side effects e.g. Console output.
Built-in Consumer functions:
Function that accepts one (Generic Type) value, and returns a Boolean.
Using a lambda like .filter(n -> f(x))
within chained Stream functions provides a predicate (true/false) result that following chained functions that would operate when the predicate returns true
.
Variations that accept primitive arguments:
Functions that receive and return the same value type.
Unary Operators ++
and --
are examples.
From W3Schools, with comments added by me:
public class unaryop {
public static void main(String[] args) {
int r = 6;
// prints 6, stores 7 into variable r
System.out.println("r=: " + r++);
// print 7
System.out.println("r=: " + r);
int x = 6;
// prints 6, stores 5 into variable x
System.out.println("x=: " + x--);
// prints 5
System.out.println("x=: " + x);
int y = 6;
// stores 7 into variable y then prints 7
System.out.println("y=: " + ++y);
int p = 6;
// stores 5 into variable p then prints 5
System.out.println("p=: " + --p);
}
}
Generally speaking:
@FunctionalInterface
annotation.Used as a final step in processing a Stream.
Useful in parallel processing.
Stream API terminal method.
Used to perform operations on mutable elements.
Processing could be many types of logic including concatenation, grouping, etc.
Import static collectors from java.util.stream.Collectors.*
.
Singly-import specific Collectors e.g. java.util.stream.Collectors.toList
.
Collectors toList and toUnmodifiableList functions:
List<T>
.List<Integer> listResult = temperatures.stream().collect(toList());
List<Integer> unmodifiableListResult = temperatures.stream().collect(toUnmodifiableList());
See Oracle Java Docs: List Interface, unmodifiableList.
Collectors toSet and toUnmodifiableSet functions:
Set<T>
.Set<Integer> temperatures = temps.stream().collect(stSet());
Collectors toCollection function:
Collection<Integer> temperatures = temps.stream().collect(toCollection(LinkedList::new));
Collectors toMap and toUnmodifiableMap functions:
keyMapper()
and valueMapper()
.Function.identity()
: Shortcut function that accepts and returns the same value.Map<String, Integer> temperatures = temps.stream().collect(toMap(Function.identity(). String::length));
Map<String, Integer> tempsWithDuplicates = temps
.stream()
.collect(toMap(Function.identity(), String::length,
(item, identicalItem) -> item // binary operator
));
Collectors collectingAndThen() function:
List<Integer> temperatures = temps.stream().collect(collectingAndThen(toList(), ImmutableList::copyOf));
Collectors joining function:
Stream<T>
elements.String concatenatedWords = words.stream().collect(joining());
String sentence = words.string().collect(joining(" "));
String alteredWords = words.stream().collect(joining(" ", "Here:", " Done!"));
returns "Here: word1 word2 Done!".Collectors counting function:
Long count = words.stream().collect(counting());
Long count = words.stream().reducing(0L, e -> 1L, Long::sum);
Collectors summarizingDouble summarizingLong summarizingInt functions:
getAverage()
, getCount()
, getMax()
, getMin()
, and getSum()
functions on the summarized Collector.Collectors averagingDouble averagingLong averagingInt functions:
String averageCityNameLength = cityNames.stream().collect(averagingDouble(String::length));
Collectors summingDouble summingLong summingInt functions:
String sumOfCityNameLengths = cityNames.stream().collect(summingDouble(String::length));
Collectors maxBy and minBy functions:
Optional<String> longestCityName = cityNames.stream().collect(maxBy(Comparator.comparing(String::length)));
Collectors groupingBy function:
Map<Integer, List<String>> cityNamesByLength = cityNames.stream().collect(groupingBy(String::length));
Collectors partitioningBy function:
Map<boolean, Collection<T>>
.Collectors teeing function:
numbers.stream().collection(teeing(minBy(Integer::compareTo), maxBy(Integer::compareTo), (min, max) -> min + ", " + max));
Implement the Collector
interface: public interface Collector<T, A, R> { ... }
.
implements Collector<T, A, R>
where A and R are defined like ImmutableSet.Builder<T>
and ImmutableSet<T>
respectively (as an example).supplier()
, accumulator()
, combiner()
, finisher()
and characteristics()
functions, using @Override
annotation.characteristics()
function:
UNORDERED
characteristic tells the Stream that the Collector does not guarantee the order of elements in the resulting Collection.IllegalStateOperation
will result when accessing a Stream that has already had a terminal operation run against it.functional style
operations.Important: Operate on a Collection not the Stream itself:
List<String> elements = Stream
.of("X", "Y", "Z")
.filter(element -> element.contains("Y"))
.collect(Collectors.toList());
Optional<String> anyElement = elements.stream().findAny();
Optional<String> firstElement = elements.stream().findFirst();
Baeldung Java Streams Links and Information.
Baeldung Java IO Tutorials (mostly about file system manipulation).
Baeldung GitHub Repo of Lambda and FunctionInterface examples
Return to ContEd Index.
Return to Root README.