☕
Optional Types
Type 📒 Lecture
Date @February 21, 2022
Lecture
1
#
Lecture
[Link]
URL
Notion [Link]
URL 45386c4ede174512955ea513e3c21c70
Week # 9
Dealing with empty streams
Largest and smallest values seen
max() and min()
Requires a comparison function
What happens if the stream is empty?
Optional<Double> maxrand =
[Link](Math::random)
Optional Types 1
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);
max() of empty stream is undefined
Return value could be Double or null
Optional<T> object
Wrapper
May contain an object of type T
Value is present
Or no object
Handling missing optional values
Or orElse() to pass a default value (if the desired value is not present)
Optional<Double> maxrand =
[Link](Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);
Double fixrand = [Link](-1.0);
Use orElseGet() to call a function which generates replacement for a missing
value
Optional<Double> maxrand =
[Link](Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);
Double fixrand = [Link](
() -> SomeFunctionToGenerateDouble
);
Use orElseThrow() to generate an exception when a missing value is
encountered
Optional Types 2
Optional<Double> maxrand =
[Link](Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);
Double fixrand = [Link](
IllegalStateException::new
);
Ignoring missing values
Use ifPresent() to test if a value is present, and process it
Missing value is ignored
[Link](v -> Process v);
For instance, add maxrand to a collection results , if it is present
Optional<Double> maxrand =
[Link](Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);
var results = new ArrayList<Double>();
[Link](v -> [Link](v));
Or, we can pass the function in different forms as well
Optional<Double> maxrand =
[Link](Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);
var results = new ArrayList<Double>();
[Link](results::add);
Specify an alternative action if the value is not present
Optional<Double> maxrand =
[Link](Math::random)
.limit(100)
Optional Types 3
.filter(n -> n < 0.001)
.max(Double::compareTo);
var results = new ArrayList<Double>();
[Link](
v -> [Link](v),
() -> [Link]("No max")
);
Creating an optional value
Create an optional value
[Link](v) creates value v
[Link] creates empty optional
public static Optional<Double> inverse(Double x) {
if(x == 0) {
return [Link]();
} else {
return [Link](1 / x);
}
}
Use ofNullable() to transform null automatically into an empty optional
Useful when working with functions that return object of type T or null ,
rather than Optional<T>
Example, the above code will produce a result if x is not 0 otherwise, it will
return empty
public static Optional<Double> inverse(Double x) {
return [Link](1 / x);
}
Passing on optional values
Can produce an output Optional value from an input Optional
map applies function to value, if present
If input is empty, so is output
Optional Types 4
Optional<Double> maxrand =
[Link](Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);
Optional<Double> maxrandasqr =
[Link](v -> v * v);
Another example
Optional<Double> maxrand =
[Link](Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);
var results = new ArrayList<Double>();
[Link](results::add);
Supply an alternative for a missing value
If value is present, it is passed as is
If value is empty, value generated by or() is passed
Optional<Double> maxrand =
[Link](Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);
Optional<Double> fixrand =
[Link](() -> [Link](-1.0));
Composing optional values of different types
Suppose that
f() returns Optional<T>
Class T defines g() , returning Optional<U>
Cannot compose s.f().g()
s.f() has type Optional<T> , not T
Optional Types 5
Instead, use flatMap
s.f().flatMap(T::g)
If s.f() is present, apply g()
Otherwise return empty Optional<U>
Optional<U> result = s.f().flatMap(T::g);
For example, pass output of earlier safe inverse() to safe SquareRoot()
public static Optional<Double> inverse(Double x) {
if(x == 0) {
return [Link]();
} else {
return [Link](1/x);
}
}
public static Optional<Double> squareRoot(Double x) {
if(x < 0) {
return [Link]();
} else {
return [Link]([Link](x));
}
}
Optional<Double> result = inverse(x).flatMap(MyClass::squareRoot);
Turning an optional into a stream
Suppose lookup(u) returns a User if u is a valid username
Optional<User> lookup(String id) { ... }
We want to convert a stream of userids into a stream of users
Input is Stream<String>
Output is Stream<User>
But lookup returns Optional<User>
Pass through a flatMap
Optional Types 6
Stream<String> ids = ...;
Stream<User> users =
[Link](Users::lookup)
.flatMap(Optional::stream);
What if lookup was implemented without using Optional ?
oldLookup returns User or null
Use ofNullable to regenerate Optional<User>
Stream<String> ids = ...;
Stream<User> users = [Link](
id -> [Link](
[Link](id)
)
);
Summary
Optional<T> is a clean way to encapsulate a value that may be absent
Different ways to process values of type Optional<T>
Replace the missing value by a default
Ignore missing values
Can create values of type Optional<T> where outcome may be undefined
Can write functions that transform optional values to optional values
flatMap allows us to cascade functions with optional types
Use flatMap to regenerate a stream from optional values
Optional Types 7
☕
Collecting results from Streams
Type 📒 Lecture
Date @February 21, 2022
Lecture # 2
Lecture [Link]
URL
Notion [Link]
URL 65c4b17d4cf44d7b83c48c486776e3a0
Week
9
#
Collecting values from a stream
Convert collections into sequences of values — streams
Process a stream as a collection?
Stream definees a standard iterator, use to loop through values in a stream
Alternatively, use forEach with a suitable function
Can convert a stream into an array usint toArray()
Collecting results from Streams 1
Creates an array of Object by default
Pass array constructor to get a more specific array type
[Link]([Link]::println);
Object[] result = [Link]();
String[] result = [Link](String[]::new);
// [Link]() has the type Object[]
Storing a stream as a collection
What if we want to convert the stream back into a collection?
Use collect()
Pass appropriate factory method from Collectors
Static method that directly calls a constructor
Implicitly creates an object
Create a list from a stream
List<String> result = [Link]([Link]());
... or a set
Set<String> result = [Link]([Link]());
To create a concrete collection, provide a constructor
TreeSet<String> result = [Link](
[Link](TreeSet::new)
);
Stream summaries
We saw how to reduce a stream to a single result value — count(), max(), ...
In general, need a stream of numbers
Collectors has methods to aggregate summaries in a single object
Collecting results from Streams 2
summarizingInt works for a stream of integers
Pass function to convert given stream to numbers — here String::length
Returns IntSummaryStatistics that stores count, max, min, sum, average
IntSummaryStatistics summary =
[Link](
[Link](String::length);
);
double averageWordLength = [Link]();
double maxWordLength = [Link]();
Methods to access relevant statistics
getCount()
getMax()
getMin()
getSum()
getAverage()
Similarly, summarizingLong() and summarizingDouble() return LongSummaryStatistics
and DoubleSummaryStatistics
Converting a stream to a map
Convert a stream of Person to a map
For Person p , [Link]() is the key and [Link]() is value
Stream<Person> people = ...;
Map<Integer, String> idToName =
[Link](
[Link](
Person::getId,
Person::getName
)
);
To store entire object as value, use [Link]()
Stream<Person> people = ...;
Map<Integer, Person> idToPerson =
Collecting results from Streams 3
[Link](
[Link](
Person::getId,
[Link]()
)
);
What happens if we use name for key and id for value?
Likely to have duplicate keys — IllegalStateException
Stream<Person> people = ...;
Map<String, Integer> nameToID =
[Link](
[Link](
Person::getName,
Person::getId
)
);
Provide a function to fix such problems
Stream<Person> people = ...;
Map<String, Integer> nameToID =
[Link](
[Link](
Person::getName,
Person::getId,
(existingValue, newValue) -> existingValue
)
);
Grouping and partitioning values
Instead of discarding values with duplicate keys, group them
Collect all ids with the same name in a list
Instead, may want to partition the stream using a predicate
Stream<Person> people = ...;
Map<String, List<Person>> nameToPersons =
[Link](
[Link](
Person::getName
)
);
Collecting results from Streams 4
Partition names into those that start with A and the rest
Key values of resulting map are true and false
Stream<Person> people = ...;
Map<String, List<Person>> aAndOtherPersons =
[Link](
[Link](
p -> [Link]().substr(0,1).equals("A")
)
);
List<Person> startingLetterA = [Link](true);
Summary
We converted collections into sequences and processed them as streams
After transformations, we may want to process a stream as a collection
Use iterators, forEach() to process a stream element by element
Use toArray() to convert to an array
Factory methods in Collector allows us to convert a stream back into a
collection of our choice
Can convert an arbitrary stream into a stream of numbers and collect summary
statistics
Can convert a stream into a map
Can group values by a key, or partition by a predicate
Collecting results from Streams 5
☕
Input/Output Streams
Type 📒 Lecture
Date @February 21, 2022
Lecture
3
#
Lecture
[Link]
URL
Notion [Link]
URL c35b40f4d8ce4385abf3ec9b190a4e5e
Week # 9
Input and Output streams
Input → read a sequence of bytes from some source
A file, an internet connection, memory, ...
Output → write a sequence of bytes to some source
A file, an internet connection, memory, ...
Java refers to these as input and output streams
Not the same as stream objects in class Stream
Input/Output Streams 1
Input and Output values could be of different types
Ultimately, input and output are raw uninterpreted bytes of data
Interpret as text — different Unicode encodings
Or as binary data — integers, floats, doubles, ...
Use a pipeline of input/output stream transformers
Read raw bytes from a file, pass to a stream that reads text
Generate binary data, pass to a stream that writes raw bytes to a file
Reading and Writing raw bytes
Classes InputStream and OutputStream
Read one or more bytes — abstract methods are implemented by subclasses of
InputStream
Check availability before reading
abstract int read();
int read(byte[] b);
byte[] readAllBytes();
// ... and more
InputStream in = ....
int bytesAvailable = [Link]();
if(bytesAvailable > 0) {
var data = new byte[bytesAvailable];
[Link](data);
}
Write bytes to output
Close a stream when done — release resources
Flush an output stream — output is buffered
abstract void write(int b);
void write(byte[] b);
// ... and more
OutputStream ot = ...
byte[] values = ...;
[Link](values);
[Link]();
Input/Output Streams 2
[Link]();
Connecting a stream to an external source
Input and output streams ultimately connect to external resources
A file, an internet connection memory ...
We limit ourselves to files
Create an input stream attached to a file
Create an output stream attached to a file
Overwrite or append?
Pass a boolean second argument to the constructor
var in = new FileInputStream("[Link]");
var out = new FileInputStream("[Link]");
// Overwrite
var out = new FileOutputStream("[Link]", false);
// Append
var out = new FileOutputStream("[Link]", true);
Reading and Writing text
Recall Scanner class
Can apply to any input stream
Many read methods
var fin = new FileInputStream("[Link]");
var scin = new Scanner(fin);
String s = [Link](); // One line
String w = [Link](); // One word
int i = [Link](); // Read an int
boolean b = [Link](); // Any more words?
To write text, use PrintWriter class
Apply to any output stream
Input/Output Streams 3
var fout = new FileOutputStream("[Link]");
var pout = new PrintWriter(fout);
Use println() , print() to write txt
String msg = "Hello, World!";
[Link](msg);
Example: Copy input text file to output text file
var in = new Scanner(...);
var out = new PrintWriter(...);
while([Link]()) {
String line = [Link]();
[Link](line);
}
Beware: input/output methods generate many different kinds of exceptions
Need to wrap code with try blocks
Reading and Writing binary data
To read binary data, use DataInputStream class
Can apply to any input stream
Many read methods
var fin = new FileInputStream("[Link]");
var din = new DataInputStream(fin);
readInt, readShort, readLong
readFloat, readDouble
readChar, readUTF
readBoolean
To write binary data, use DataOutputStream class
Apply to any output stream
Many write methods
Input/Output Streams 4
var fout = new FileOutputStream("[Link]");
var dout = new DataOutputStream(fout);
writeInt, writeShort, writeLong
writeFloat, writeDouble
writeChar, writeUTF
writeBoolean
writeChars
writeByte
Example: Copy input binary file to output binary file
Catch exceptions
var in = new DataInputStream(...);
var out = new DataOutputStream(...);
int bytesAvailable = [Link]();
while(bytesAvailable > 0) {
var data = new byte[bytesAvailable];
[Link](data);
[Link](data);
bytesAvailable = [Link]();
}
Other features
Buffering an input stream
Reads blocks of data
More efficient
var din = new DataInputStream(
new BufferedInputStream(
new FileInputStream("[Link]")
)
);
Speculative reads
Examine the first element
Return to stream if necessary
var pbin = new PushbackInputStream(
new BufferedInputStream(
Input/Output Streams 5
new FileInputStream("[Link]")
)
);
int b = [Link]();
if(b != '<') {
[Link](b);
}
Streams are specialized
PushBackStream can only read() and unread()
Feed to a DataInputStream to read meaningful data
var pbin = new PushbackInputStream(
new BufferedInputStream(
new FileInputStream("[Link]")
)
);
var din = new DataInputStream(pbin);
Java has a whole zoo of streams for different tasks
Random access files, zipped data, ...
Chain together streams in a pipeline
Read binary data from a zipped file
FileInputStream
ZipInputStream
DataInputStream
Summary
Java’s approach to input/output is to separate out concerns
Chain together different types of input/output streams
Connect an external source as input or output
Read and Write raw bytes
Interpret raw bytes as text
Interpret raw bytes as data
Buffering, speculative read, random access files, zipped data, ...
Input/Output Streams 6
Chaining together streams appears tedious, but adds flexibility
Input/Output Streams 7
☕
Serialization
Type 📒 Lecture
Date @February 22, 2022
Lecture # 4
Lecture
[Link]
URL
Notion [Link]
URL ca1d3636d5584ebca8f284baa577fb12
Week # 9
Reading and Writing Objects
We can read and write binary data
DataInputStream , DataOutputStream
Read and write low level units
Bytes, integers, floats, characters, ...
Can we export and import objects directly?
Why would we want to do this?
Serialization 1
Backup objects onto disk, with state
Restore objects from the disk
Send objects across a network
Serialization and Deserialization
Reading and writing objects ...
To write objects, Java has another output stream type, ObjectOutputStream
var out = new ObjectOutputStream(
new FileOutputStream("[Link]")
);
Use writeObject() to write out an object
var emp = new Employee(...);
var boss = new Manager(...);
[Link](emp);
[Link](boss);
To read back objects, use ObjectInputStream
var in = new ObjectInputStream(
new FileInputStream("[Link]")
);
Retrieve objects in the same order they were written using readObject()
var e1 = (Employee) [Link]();
var e2 = (Employee) [Link]();
Class has to allow serialization — implement marker interface Serializable
public class Employee implements Serializable { ... }
How serialization works
ObjectOutputStream examines all the fields and saves their contents
Serialization 2
ObjectInputStream "reconstructs" the object, effectively calls a constructor
What happens when many objects share the same object as an instance
variable?
class Manager extends Employee {
private Employee secretary;
...
}
Two managers have the same secretary
Each object is assigned a serial number — hence, serialization
When first encountered, save the data to output stream
If saved previously, record the serial number
Reverse the process when reading
Customizing serialization
Some objects should not be serialized — value of file handles, ...
Mark such fields as transient
public class LabeledPoint implements Serializable {
private String label;
private transient [Link] point;
...
}
Can override writeObject()
defaultWriteObject() writes out the object with all non-transient fields
Then explicitly write relevant details of transient fields
private void writeObject(ObjectOutputStream out)
throws IOException {
[Link]();
[Link]([Link]());
[Link]([Link]());
}
... and readObject()
Serialization 3
defaultReadObject() reconstructs object with all non-transient fields
Then explicitly reconstruct transient fields
private void readObject(ObjectInputStream in)
throws IOException {
[Link]();
double x = [Link]();
double y = [Link]();
point = new [Link](x, y);
}
Handle with care
Serialization is a good option to share data within an application
Over time, older serialized objects may be incompatible with newer versions
Some mechanisms for version control, but still some pitfalls possible
Deserialization implicitly invokes a constructor
Running code from an external source
Always a security risk
Summary
Serialization allows us to export and import objects, with state
Backup objects onto disk, with state
Restore objects from disk
Send objects across a network
Use ObjectOutputStream and ObjectInputStream to write and read objects
Serial numbers are used to ensure only a single copy of each shared object is
archived
Mark fields that should not be serialized as transient
Customize writeObject() and readObject()
Serialization carries risks of ...
Version control of objects
Running unknown code
Serialization 4