diff --git a/src/main/java/fi/utu/tech/ooj/exercise3/Kirja.java b/src/main/java/fi/utu/tech/ooj/exercise3/Kirja.java
index 13d735cebecb2492da4cdc2f967aed46ea347313..a9cfe5f8508a07b201ea75d595556d7628c9cbc4 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise3/Kirja.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/Kirja.java
@@ -1,6 +1,6 @@
 package fi.utu.tech.ooj.exercise3;
 
-public class Kirja {
+public class Kirja implements Cloneable{
     private String kirjanNimi;
     private String kirjailijanNimi;
     private int julkaisuVuosi;
@@ -35,5 +35,28 @@ public class Kirja {
         this.julkaisuVuosi = julkaisuVuosi;
     }
 
+    @Override
+    public Kirja clone()
+    {
+        return new Kirja(kirjanNimi, kirjailijanNimi, julkaisuVuosi);
+    }
+
+    @Override
+    public String toString()
+    {
+        return kirjanNimi + ", " + kirjailijanNimi + ", " + julkaisuVuosi;
+    }
 
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj == null) return false;
+        if (obj == this) return true;
+        if (!(obj instanceof Kirja)) return false;
+
+        Kirja toinen = (Kirja)obj;
+        return  toinen.kirjanNimi.equals(kirjanNimi) &&
+                toinen.kirjailijanNimi.equals(kirjailijanNimi) &&
+                toinen.julkaisuVuosi == julkaisuVuosi;
+    }
 }
diff --git a/src/main/java/fi/utu/tech/ooj/exercise3/KirjaKokoelma.java b/src/main/java/fi/utu/tech/ooj/exercise3/KirjaKokoelma.java
index 285fd40c2145f3fa3ce10f18412afb657fe48f5e..5f681f3f836a8b992d31c1410e9d428e5ef4e3f8 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise3/KirjaKokoelma.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/KirjaKokoelma.java
@@ -4,7 +4,7 @@ import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.List;
 
-public class KirjaKokoelma {
+public class KirjaKokoelma implements Cloneable{
 
     private String kokoelmanNimi;
     private  ArrayList<Kirja>kirjaListaus;
@@ -30,4 +30,56 @@ public class KirjaKokoelma {
         kirjaListaus.add(teos);
     }
 
+    @Override
+    public KirjaKokoelma clone()
+    {
+        KirjaKokoelma kopio = new KirjaKokoelma(kokoelmanNimi);
+        for (Kirja kirja : kirjaListaus)
+        {
+            kopio.lisaaKirja(kirja);
+        }
+        return kopio;
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder palautus = new StringBuilder();
+        palautus.append(kokoelmanNimi).append("\n");
+        for (Kirja kirja : kirjaListaus)
+        {
+            palautus.append(kirja.toString()).append("\n");
+        }
+        return palautus.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj == null) return false;
+        if (obj == this) return true;
+        if (!(obj instanceof KirjaKokoelma)) return false;
+
+        KirjaKokoelma toinen = (KirjaKokoelma)obj;
+
+        if (!(toinen.kokoelmanNimi.equals(kokoelmanNimi)) ||
+                toinen.kirjaListaus.size() != kirjaListaus.size()) return false;
+
+        for (Kirja kirja : toinen.kirjaListaus)
+        {
+            boolean on = false;
+            for (Kirja kirja1 : kirjaListaus)
+            {
+                if (kirja1.equals(kirja)) {
+                    on = true;
+                    break;
+                }
+            }
+            if (!on)
+            {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/src/main/java/fi/utu/tech/ooj/exercise3/Kirjasto.java b/src/main/java/fi/utu/tech/ooj/exercise3/Kirjasto.java
index 2478ff6351648daef8b963f175230b13d1aa3569..018abe04894aeea6b4997fea4a1c72f1e8b3400a 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise3/Kirjasto.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/Kirjasto.java
@@ -5,7 +5,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
-public class Kirjasto {
+public class Kirjasto implements Cloneable {
 
     private String kirjastonNimi;
     private String osoite;
@@ -57,5 +57,85 @@ public class Kirjasto {
         sivukirjastot.add(input);
     }
 
+    @Override
+    public Kirjasto clone()
+    {
+        Kirjasto kopio = new Kirjasto(kirjastonNimi, osoite);
+        for (KirjaKokoelma kokoelma : kokoelmat)
+        {
+            kopio.lisaaKokoelma(kokoelma.clone());
+        }
+        for (Kirjasto kirjasto : sivukirjastot)
+        {
+            kopio.lisaaSivukirjasto(kirjasto.clone());
+        }
+        return kopio;
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder palautus = new StringBuilder();
+        palautus.append(kirjastonNimi).append("\n");
+        palautus.append(osoite).append("\n");
+        palautus.append("Kokoelmat:").append("\n");
+        for (KirjaKokoelma kokoelma : kokoelmat)
+        {
+            palautus.append(kokoelma.toString()).append("\n");
+        }
+        palautus.append("Sivukirjastot:").append("\n\n");
+        for (Kirjasto kirjasto : sivukirjastot)
+        {
+            palautus.append(kirjasto.toString());
+        }
+        return palautus.toString();
+    }
 
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj == null) return false;
+        if (obj == this) return true;
+        if (!(obj instanceof Kirjasto)) return false;
+
+        Kirjasto toinen = (Kirjasto)obj;
+
+        if (!(toinen.kirjastonNimi.equals(kirjastonNimi) &&
+            toinen.osoite.equals(osoite))) return false;
+
+        if (toinen.kokoelmat.size() != kokoelmat.size()) return false;
+        for (KirjaKokoelma kokoelma : toinen.kokoelmat)
+        {
+            boolean on = false;
+            for (KirjaKokoelma kokoelma1 : kokoelmat)
+            {
+                if (kokoelma1.equals(kokoelma)) {
+                    on = true;
+                    break;
+                }
+            }
+            if (!on)
+            {
+                return false;
+            }
+        }
+
+        if (toinen.sivukirjastot.size() != sivukirjastot.size()) return false;
+        for (Kirjasto kirjasto : toinen.sivukirjastot)
+        {
+            boolean on = false;
+            for (Kirjasto kirjasto1 : sivukirjastot)
+            {
+                if (kirjasto1.equals(kirjasto)) {
+                    on = true;
+                    break;
+                }
+            }
+            if (!on)
+            {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/src/main/java/fi/utu/tech/ooj/exercise3/Main.java b/src/main/java/fi/utu/tech/ooj/exercise3/Main.java
index 0c5fb2a608860ddf2225d92690eafd616fb4c96c..f5aec7c321494cda42135cbf959921407f0332dc 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise3/Main.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/Main.java
@@ -14,7 +14,7 @@ public class Main {
      */
 
 
-    public static void main(String[] args) {
+    public static void main(String[] args){
         /*
          * Testin jälkeen alla olevan rivin voi joko kommentoida tai poistaa.
          */
@@ -24,5 +24,84 @@ public class Main {
         HashSet<HenkiloAuto> henkiloautot = new HashSet<HenkiloAuto>();
         HashSet<KuormaAuto> kuormaautot = new HashSet<KuormaAuto>();
 
+
+        // ---Tehtävä 2 demonstrointi---
+        System.out.println("---Tehtävä 2---");
+        Kirjasto kirjasto = new Kirjasto("Turku", "Linnankatu");
+        //sivukirjastot
+        Kirjasto kirjasto1 = new Kirjasto("Feeniks", "Yliopisto");
+        Kirjasto kirjasto2 = new Kirjasto("Nummi", "Halistentie");
+        //kokoelmat
+        KirjaKokoelma[] kokoelmat = {
+                new KirjaKokoelma("fantasia"),
+                new KirjaKokoelma("scifi"),
+                new KirjaKokoelma("kauhu"),
+                new KirjaKokoelma("draama"),
+                new KirjaKokoelma("trilleri"),
+                new KirjaKokoelma("rikos")
+        };
+        //lisätään kirjat kokoelmiin
+        int indeksi = 1;
+        for (KirjaKokoelma kokoelma : kokoelmat)
+        {
+            for (int i = 0; i < 2; i++) {
+                kokoelma.lisaaKirja(new Kirja(
+                        "Kirja" + indeksi,
+                        "Kirjailija" + indeksi,
+                        indeksi));
+                indeksi++;
+            }
+        }
+        // kokoelmat kirjastoihin
+        kirjasto.lisaaKokoelma(kokoelmat[0]);
+        kirjasto.lisaaKokoelma(kokoelmat[1]);
+        kirjasto1.lisaaKokoelma(kokoelmat[2]);
+        kirjasto1.lisaaKokoelma(kokoelmat[3]);
+        kirjasto2.lisaaKokoelma(kokoelmat[4]);
+        kirjasto2.lisaaKokoelma(kokoelmat[5]);
+        //kirjastot sivukirjastoiksi
+        kirjasto.lisaaSivukirjasto(kirjasto1);
+        kirjasto.lisaaSivukirjasto(kirjasto2);
+
+        System.out.println(kirjasto.toString());
+
+        Kirjasto kopio = kirjasto.clone();
+        System.out.println(kirjasto.equals(kopio));
+
+
+        // ---tehtävä 3 demonstrointi---
+        System.out.println("---Tehtävä 3---");
+        HashMap<String, String> map = new HashMap<>();
+        map.put("Hei", "Heippa");
+        map.put("Moi", "Moikka");
+        RandomMap rmap = new RandomMap<>(map);
+        System.out.println(rmap.get("Hei"));
+        System.out.println(rmap.get("terve") + "\n"); // Heippa tai Moikka
+
+
+        // ---tehtävä 4 demonstrointi---
+        System.out.println("---Tehtävä 4---");
+        Triplet<String, Integer, Double> triplet = new Triplet<>();
+        triplet.put("avain1", 1, 1.11);
+        triplet.put("avain2", 2, 2.22);
+        triplet.put("avain3", 3, 3.33);
+
+        System.out.println(triplet.getFirstValue("avain1"));
+        System.out.println(triplet.getSecondValue("avain3") + "\n");
+
+        System.out.println(triplet.keySet());
+        System.out.println(triplet.firstValues());
+        System.out.println(triplet.secondValues() + "\n");
+
+        System.out.println(triplet.containsKey("avain2")); // true
+        System.out.println(triplet.containsKey("avain4")); // false
+        System.out.println(triplet.containsValue(1, 1.11)); // true
+        System.out.println(triplet.containsValue(4, 4.44) + "\n"); // false
+
+        System.out.println(triplet.isEmpty()); // false
+        System.out.println(triplet.size());
+        triplet.remove("avain3");
+        triplet.clear();
+        System.out.println(triplet.isEmpty()); // true
     }
 }
diff --git a/src/main/java/fi/utu/tech/ooj/exercise3/RandomMap.java b/src/main/java/fi/utu/tech/ooj/exercise3/RandomMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee9681dcc150a5de3cec3e13615a09e736c72814
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/RandomMap.java
@@ -0,0 +1,75 @@
+package fi.utu.tech.ooj.exercise3;
+
+import java.util.*;
+
+public class RandomMap <T extends Map> implements Map{
+
+    T map;
+
+    public RandomMap(T map)
+    {
+        this.map = map;
+    }
+    @Override
+    public int size() {
+        return map.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object o) {
+        return map.containsKey(o);
+    }
+
+    @Override
+    public boolean containsValue(Object o) {
+        return map.containsValue(o);
+    }
+
+    @Override
+    public Object get(Object o) {
+        if (map.get(o) != null) return map.get(o);
+        ArrayList<Object> arvot = new ArrayList<Object>(map.values());
+        Collections.shuffle(arvot);
+        return arvot.get(0);
+    }
+
+    @Override
+    public Object put(Object o, Object o2) {
+        return map.put(o, o2);
+    }
+
+    @Override
+    public Object remove(Object o) {
+        return map.remove(o);
+    }
+
+    @Override
+    public void putAll(Map map1) {
+        map.putAll(map1);
+    }
+
+    @Override
+    public void clear() {
+        map.clear();
+    }
+
+    @Override
+    public Set keySet() {
+        return map.keySet();
+    }
+
+    @Override
+    public Collection values() {
+        return map.values();
+    }
+
+    @Override
+    public Set<Entry> entrySet() {
+        return map.entrySet();
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise3/Triplet.java b/src/main/java/fi/utu/tech/ooj/exercise3/Triplet.java
new file mode 100644
index 0000000000000000000000000000000000000000..d76c989151de5721b32991951a478c33c2eb59cc
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/Triplet.java
@@ -0,0 +1,90 @@
+package fi.utu.tech.ooj.exercise3;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Triplet <K, V, W> implements TripletMap<K, V, W>{
+
+    private HashMap<K, Object[]> map;
+
+    public Triplet()
+    {
+        map = new HashMap<>();
+    }
+
+    @Override
+    public void put(K key, V value1, W value2) {
+        map.put(key, new Object[]{value1, value2});
+    }
+
+    @Override
+    public V getFirstValue(K key) {
+        return (V)map.get(key)[0];
+    }
+
+    @Override
+    public W getSecondValue(K key) {
+        return (W)map.get(key)[1];
+    }
+
+    @Override
+    public void remove(K key) {
+        map.remove(key);
+    }
+
+    @Override
+    public void clear() {
+        map.clear();
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return map.keySet();
+    }
+
+    @Override
+    public Set<V> firstValues() {
+        Set<V> fValues = new HashSet<>();
+        for (Object[] pari : map.values())
+        {
+            fValues.add((V)pari[0]);
+        }
+        return fValues;
+    }
+
+    @Override
+    public Set<W> secondValues() {
+        Set<W> sValues = new HashSet<>();
+        for (Object[] pari : map.values())
+        {
+            sValues.add((W)pari[1]);
+        }
+        return sValues;
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return map.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value1, Object value2) {
+        for (Object[] pari : map.values())
+        {
+            if (pari[0].equals(value1) && pari[1].equals(value2)) return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
+    @Override
+    public int size() {
+        return map.size();
+    }
+
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise3/TripletMap.java b/src/main/java/fi/utu/tech/ooj/exercise3/TripletMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..63a9e2578de39207a849ba9a1b967899f6beeeab
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/TripletMap.java
@@ -0,0 +1,21 @@
+package fi.utu.tech.ooj.exercise3;
+
+import java.util.Set;
+
+public interface TripletMap <K, V, W>{
+
+    void put(K key, V value1, W value2);
+    V getFirstValue(K key);
+    W getSecondValue(K key);
+    void remove(K key);
+    void clear();
+
+    Set<K> keySet();
+    Set<V> firstValues();
+    Set<W> secondValues();
+    boolean containsKey(Object key);
+    boolean containsValue(Object value1, Object value2);
+    boolean isEmpty();
+    int size();
+
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise3/tehtava1 b/src/main/java/fi/utu/tech/ooj/exercise3/tehtava1
new file mode 100644
index 0000000000000000000000000000000000000000..13b9a330f6361e78d0ac09e092470c4579bd783d
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/tehtava1
@@ -0,0 +1,54 @@
+Tehtävä 1
+
+
+A. Collection ja Map
+
+Collection ja Map ovat rajapinta-luokkia. Collection-luokkaan pohjautuvat tietorakenteet
+ovat homogeenisiä, kun taas Map-luokka rakentuu avain-arvo-pareista, eli tietoa voi etsiä
+avaimen perusteella, eivätkä alkiot ole missään tietyssä järjestyksessä.
+Map-luokkien vahvuus on nopea tiedonhaku avaimen perusteella, eli jokaista alkiota ei
+tarvitse käydä erikseen läpi. Sama avain voi esiintyä vain kerran, mutta arvo voi olla
+sama kuin jokin toinen arvo.
+Collection-luokkaa voidaan käyttää kun halutaan ryhmä objekteja, johon voi listätä, poistaa
+ja hakea jäseniä. Mahdollisia ominaisuuksia ovat, että objektit pysyvät samassa järjestyksessä
+ja duplikaatteja ei sallita
+
+
+B. List, Set ja Queue
+
+Kaikki kolme kokoelmatyyppiä pohjautuu Collection-luokkaan. List-tyyppi tarjoaa dynaamisen
+indeksoidun tietorakenteen, johon voi lisätä, hakea ja poistaa alkioita vapaasti.
+List säilyttää oletuksena lisäysjärjestyksen, mutta alkioita voi poistaa tai lisätä sen
+keskelle. List sallii myös duplikaatit.
+Set-tyyppinen kokoelma on joukko uniikkeja objekteja, eli se ei salli duplikaatteja.
+Set tarjoaa myös lisäämis, poistamis ja haku operaatiot, mutta ei säilytä objekteja
+tietyssä järjestyksessä.
+Queue toimii nimen mukaisesti jonona, eli käyttäytyy first-in-first-out periaatteella.
+Objektit ovat lisäysjärjestyksessä ja uusi objekti lisätään aina jonon perälle, ja jonosta
+voi poistaa vanhimman eli jonon ensimmäisen objektin. Queue-luokka sopii tilanteeseen, missä
+tarvitaan järjestynyt jono, jota voidaan hallita.
+
+
+C. ArrayList ja LinkedList
+
+ArrayList toteuttaa List-luokan, eli se on dynaaminen ja indeksoitu taulukkolista.
+Se tarjoaa objektien lisäämisen ja poiston, sekä haun indeksin perusteella.
+LinkedList pohjautuu List- ja Queue-luokkiin. Se on linkitetty lista, eli jokaisella alkiolla
+tai "solmulla" on tieto omasta arvosta ja viittaus seuraavaan solmuun. Listaan voi lisätä ja
+siitä voi poistaa, sekä alusta, että lopusta. Listan keskeltä voi poistaa solmun, mutta
+indeksin perusteella poistaminen on hidasta, koska lista on käytävä läpi alusta.
+ArrayList sopii tilanteisiin, joissa tarvitaan tehokkaita hakutoimintoja ja sopii satunnaisiin
+lisäyksiin ja poistoihin, mutta varaa myös enemmän muistia.
+LinkedList sopii tilanteeseen, jossa satunnaista hakua ei tarvi, vaan tehokas poisto tai lisäys
+listan alusta tai lopusta.
+
+
+D. HashSet ja TreeSet
+
+HashSet ja TreeSet toteuttavat Set-rajapinnan. HashSet perustuu hajautustauluun ja tarjoaa
+nopeat yleiset operaatiot. Se ei säilytä määriteltyä järjestystä.
+TreeSet tallentaa objektit puuhun ja säilyttää järjestyksen. Lisäys, haku ja poisto toimivat
+hitaammin kuin HashSetissä, koska tiedon hallinta vaatii puun rakentamista ja järjestyksen
+ylläpitämistä. TreeSetin vahvuudet ovat siis tilanteessa, missä tarvitaan järjestettyjä
+operaatioita esim. pienimmästä suurimpaan. HashSet on hyvä tilanteessa, missä tiedonhaun
+ja tallentamisen tehokkuus on tärkeintä.
\ No newline at end of file
diff --git a/src/main/java/fi/utu/tech/ooj/exercise3/tehtava5 b/src/main/java/fi/utu/tech/ooj/exercise3/tehtava5
new file mode 100644
index 0000000000000000000000000000000000000000..93e2a96b6f64fb75baa087846dab4d1177e72327
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/tehtava5
@@ -0,0 +1,23 @@
+Tehtävä 5
+
+
+A.
+
+Signatuuri <X extends Z, Y extends Z, Z extends Ajoneuvo> tarkoittaa, että alatyypit X ja Y
+periytyvät luokasta Z, joka periytyy luokasta Ajoneuvo. Tällöin siis xs kokoelma sisältää
+Z-tyyppisiä elementtejä tai sen alatyyppiä X. ys sisältää Z tyyppiä tai alatyyppiä Y.
+Rutiini palauttaa listan Z-tyyppisenä eli se on yhteensopiva Ajoneuvo-luokan perivien
+luokkien kanssa, jotka ovat yliluokkia tyypeille Y ja X.
+
+
+B.
+
+Onnistuu, koska kummatkin luokat perivät saman luokan eli Ajoneuvon. Jos lista sisältää
+yläluokan olioita, sen perivät luokat voidaan lisätä siihen. Viittausta henkilö- ja
+kuormaautot listoihin ei voi sijoittaa, koska listojen sisältämein olioiden tyyppi on
+erilainen.
+Tilanne voidaan korjata vapaalla tyypillä:
+
+HashSet<Ajoneuvo> ajoneuvot = new HashSet<Ajoneuvo>();
+HashSet<? extends HenkiloAuto> henkiloautot = new HashSet<>();
+HashSet<? extends KuormaAuto> kuormaautot = new HashSet<>();
\ No newline at end of file