0% found this document useful (0 votes)
5 views23 pages

Week 9 Notes

The document discusses optional types in Java, emphasizing how to handle empty streams and missing values using Optional<T>. It covers methods like orElse(), ifPresent(), and flatMap for processing optional values, as well as creating and transforming them. Additionally, it introduces input/output streams, detailing how to read and write bytes from various sources, including files.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views23 pages

Week 9 Notes

The document discusses optional types in Java, emphasizing how to handle empty streams and missing values using Optional<T>. It covers methods like orElse(), ifPresent(), and flatMap for processing optional values, as well as creating and transforming them. Additionally, it introduces input/output streams, detailing how to read and write bytes from various sources, including files.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

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

You might also like