---
marp: true
title: 30 April 2021
theme: default
paginate: true

---

# <!-- fit --> CS 199 EMP

### Hosted by Jackie Chan and Akhila Ashokan

**Topics:** Generics, Streaming

---

# Resources

We'll talk about the new topics introduced this week. Won't be on the exam, but coding in general should help you perform better on the exam.

But these are some interesting topics either way! Win, win.

Write code on the homepage or any playground on the site!
https://cs125.cs.illinois.edu/

Slides are on the course site!
https://cs199emp.netlify.app/

---

# Generics and Versatility

Generics allow you to write versatile code, i.e. code that can handle multiple different types. We could do that before, but that required a *dangerous* downcast because it was interpreting everything as an `Object`.

Now, with generics, you can tell Java what datatype you want to work with!

The next page will illustrate that.

---

# Generics Example

```java
List names = new ArrayList();
names.add("Jackie");

System.out.println(names.get(0).length()); // Doesn't work!

String name = (String) names.get(0);
System.out.println(name.length()); // Works, but that's gross.
```

```java
List<String> names = new ArrayList<>(); // Java, I want Strings in this list.
names.add("Jackie");

System.out.println(names.get(0).length()); // I know what .length()!
```

---

# Practice with Generics (15 minutes)

To practice with generics, we will make a `Song` class accept a type that specifies how a review for a song will be represented, e.g. a `String` or a `Review` class that I'll provide.

Your job is to change the code so that:
* The `Song` class accepts a type for the review and input the `Review` class into the `Song` constructor.
* Add a function called `void setReview(E setReview)` to set the review for a song.
* Add a new for each song, can just be an empty review.

---

# Starter Code

```java
import java.util.List;
import java.util.ArrayList;

public class Song {
  String title;
  String artist;
  int plays;

  Song(String setTitle, String setArtist, int setPlays) {
    this.title = setTitle;
    this.artist = setArtist;
    this.plays = setPlays;
  }

  @Override
  public String toString() {
    return "\n" + this.title + " by " + this.artist + " with " + this.plays + " plays.\n";
  }
}

public class Review {
  String review;
  double rating;

  Review(String setReview, double setRating) {
    this.review = setReview;
    this.rating = setRating;
  }

  @Override
  public String toString() {
    return "\nReview: " + this.review + " Rating: " + this.rating + ".\n";
  }
}
```

---

# "Here's some data to play around with."

```java
List<Song> songs = new ArrayList<>();

songs.add(new Song("Firework", "Katy Perry", 525818627));
songs.add(new Song("Leave The Door Open", "Bruno Mars", 198934434));
songs.add(new Song("Mirrors", "Justin Timberlake", 609848428));
songs.add(new Song("One More Time", "Daft Punk", 314855132));
songs.add(new Song("Hey, Soul Sister", "Train", 816628421));
songs.add(new Song("willow", "Taylor Swift", 311590932));
songs.add(new Song("The Steps", "HAIM", 19007996));
songs.add(new Song("Your Man", "Joji", 68449071));
songs.add(new Song("HUMBLE.", "Kendrick Lamar", 1422789030));
songs.add(new Song("Royals", "Lorde", 784360393));

System.out.println(songs);
```

---

# "You'll know it works when you can run this."

```java
List<Song<Review>> songs = new ArrayList<>();
songs.add(new Song("Nights", "Frank Ocean", 342882193));
songs.get(0).setReview(new Review("Perfect song.", 5));

System.out.println(songs.get(0).review);
```

The nice thing is that `Song.review` can be anything now! It can be a `Review`, `String`, a `double`. It doesn't matter. Generics will allow you to decide!

---

# "Okay, let's try filtering." (10 minutes)

Filter the `songs` to only keep songs where the artist starts with the letter T.

Once you're done with that, try doing it with a Java streams! It's pretty neat. You'll need the following import and [documentation](https://www.baeldung.com/java-stream-filter-lambda).

```java
import java.util.stream.Collectors;
// Helps you convert a stream to a List using Collectors.toList().
```

---

# "Okay, now I want all the artists in `songs`." (10 minutes)

Give me all the artists in `songs` using an iterative approach, and then implement it using Java streams!

How are you feeling about both approaches? Which one is more fun to write? Which one is more easier to write/read?

---

# Summary

Okay, let's summarize.

* We allowed `Song` to store reviews that can be represented by anything! Anything!

* Ran through a few basic Java Stream functions that allow us to skip using loops.

Let's take it to the next step and do some sorting.

---

# Sorting Only Songs That Have An Even Number of Plays (10 minutes)

As the title says, let's sort only the songs that have an even number of plays.

Do this iteratively and then use Java Streams. You'll probably figured out by now that Streams can save you a lot of stress.

Sort using the name of the artist.

To sort an `ArrayList`, you'll need `import java.util.Collections;`

---

# That's It!

I was learning Java streams at the same time. I think they're awesome. They make Java code much more readable in my opinion, but you do need to look around for documentation and get used to the style (a functional style).

You're so close to the end!

Sorry about the problems being kind of convoluted, but I hope it illustrates how beautiful streams are and how powerful generics can be.

As always, do something fun this weekend. Stay safe, keep in mind your physical and mental wellbeing.

---

# Solution Section

---

# Generic Song Review Solution

```java
public class Song<E> {
  String title;
  String artist;
  int plays;
  E review;
  
  Song(String setTitle, String setArtist, int setPlays) {
    this.title = setTitle;
    this.artist = setArtist;
    this.plays = setPlays;
  }
  
  public void setReview(E setReview) {
    this.review = setReview;
  }
  
  @Override
  public String toString() {
    String output = "\n" + this.title + " by " + this.artist + " with " + this.plays + " plays.\n";
    output += review + "\n";
    return output;
  }
}
```

---

# Adding Reviews To Songs Solution

```java
for (int i = 0; i < songs.size(); i++) {
  songs.get(i).review = new Review("Pretty good!", 4);
}
```

---

# Filtering Iteratively Solution

```java
List<Song> filteredSongs = new ArrayList<>();

for (int i = 0; i < songs.size(); i++) {
  if (songs.get(i).artist.startsWith("T")) {
    filteredSongs.add(songs.get(i));
  }
}

System.out.println(filteredSongs);
```

---

# Filtering Using Java Streams

```java
List<Song> filteredSongs = songs
    .stream()
    .filter(s -> s.artist.startsWith("T"))
    .collect(Collectors.toList());

System.out.println(filteredSongs);
```

---

# Getting Artists Solution

Iteratively.

```java
List<String> artists = new ArrayList<>();

for (Song s : songs) {
  artists.add(s.artist);
}

System.out.println(artists);
```

With Streams.

```java
List<String> artists = songs
    .stream()
    .map(s -> s.artist)
    .collect(Collectors.toList());
    
System.out.println(artists);
```

---

# Sorting Artists with Even Plays Iteratively Solution

Add to `Song`.
```java
public class Song<E> implements Comparable<Song> {
  @Override
  public int compareTo(Song s) {
    return this.artist.compareTo(s.artist);
  }
}
```

```java
List<Song> evenSongs = new ArrayList<>();

for (Song s : songs) {
  if (s.plays % 2 == 0) {
    evenSongs.add(s);
  }
}

Collections.sort(evenSong);
```

---

# Sorting Artists with Even Plays Using Streams

```java
List<Song> evenSongs = songs
    .stream()
    .filter(s -> s.plays % 2 == 0)
    .sorted((s1, s2) -> s1.artist.compareTo(s2.artist))
    .collect(Collectors.toList());
    
System.out.println(evenSongs);
```