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

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.
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

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."

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."

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.

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

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

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

Filtering Iteratively Solution

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

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

System.out.println(filteredSongs);

Getting Artists Solution

Iteratively.

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

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

System.out.println(artists);

With Streams.

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.

public class Song<E> implements Comparable<Song> {
  @Override
  public int compareTo(Song s) {
    return this.artist.compareTo(s.artist);
  }
}
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

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);