From b23f5da223d4092d00bd4045e47e3092628e59be Mon Sep 17 00:00:00 2001
From: Oiva Mickelsson <oomick@utu.fi>
Date: Sun, 21 May 2023 16:13:32 +0300
Subject: [PATCH] =?UTF-8?q?toteutettiin=20teht=C3=A4v=C3=A4t?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../fi/utu/tech/ooj/exercise3/Ajoneuvo.java   |   5 +-
 .../java/fi/utu/tech/ooj/exercise3/Kirja.java |  26 ++-
 .../utu/tech/ooj/exercise3/KirjaKokoelma.java |  48 ++++-
 .../fi/utu/tech/ooj/exercise3/Kirjasto.java   |  67 ++++++-
 .../java/fi/utu/tech/ooj/exercise3/Main.java  | 105 ++++++++++-
 .../fi/utu/tech/ooj/exercise3/RandomMap.java  |  89 +++++++++
 .../fi/utu/tech/ooj/exercise3/Triplet.java    | 169 ++++++++++++++++++
 .../fi/utu/tech/ooj/exercise3/TripletMap.java |  39 ++++
 .../fi/utu/tech/ooj/exercise3/tehtava1.txt    |  30 ++++
 9 files changed, 564 insertions(+), 14 deletions(-)
 create mode 100644 src/main/java/fi/utu/tech/ooj/exercise3/RandomMap.java
 create mode 100644 src/main/java/fi/utu/tech/ooj/exercise3/Triplet.java
 create mode 100644 src/main/java/fi/utu/tech/ooj/exercise3/TripletMap.java
 create mode 100644 src/main/java/fi/utu/tech/ooj/exercise3/tehtava1.txt

diff --git a/src/main/java/fi/utu/tech/ooj/exercise3/Ajoneuvo.java b/src/main/java/fi/utu/tech/ooj/exercise3/Ajoneuvo.java
index 7eaa5d5..3798d77 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise3/Ajoneuvo.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/Ajoneuvo.java
@@ -29,7 +29,10 @@ abstract class Ajoneuvo {
     public void setOmistaja(String omistaja) {
         this.omistaja = omistaja;
     }
-
+//Tässä esimerkissä Z sallii vain ajoneuvo-tyyppisiä tyyppiparametreja. X ja Y tyyppiparametrit sallivat, että
+//tyypit voivat myös olla ajoneuvon alityyppejä. Eli X:n ja Y:n on oltava Z:an eli AjoNeuvon alityyppejä, jotta
+//listojen yhdistäminen voisi olla mahdollista. Listaan tällöin on mahdollista ainoastaan lisätä tyyppejä, jotka
+//ovat, joko samat kuin X ja Y tai X:n ja Y:n perijät.
     public static <X extends Z, Y extends Z, Z extends Ajoneuvo> Set<Z> yhdista(Set<X> xs, Set<Y> ys){
         var tmp = new TreeSet<Z>();
         for (var x : xs) tmp.add(x);
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 13d735c..ab7e98e 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;
@@ -10,7 +10,29 @@ public class Kirja {
         this.kirjailijanNimi = kirjailijanNimi;
         this.julkaisuVuosi = julkaisuVuosi;
     }
-
+    @Override
+    public Kirja clone() throws CloneNotSupportedException {
+        //Tähän riittää pintakopio
+        return (Kirja)super.clone();
+    }
+    @Override
+    public String toString() {
+        return "kirjanNimi: " + getKirjanNimi() + " kirjalijanNimi: " + getKirjailijanNimi() + " julkaisuVuosi: " + getJulkaisuVuosi() + " ";
+    }
+    @Override
+    public boolean equals(Object toinen) {
+        if (toinen == null) {
+            return false;
+        }
+        if (toinen == this) {
+            return true;
+        }
+        if (!(toinen instanceof  Kirja)) { //toinen.getClass() != getClass()
+            return false;
+        }
+        Kirja k = (Kirja)toinen;
+        return k.kirjanNimi.equals(kirjanNimi) && k.kirjailijanNimi.equals(kirjailijanNimi) && k.julkaisuVuosi == julkaisuVuosi;
+    }
     public String getKirjanNimi() {
         return kirjanNimi;
     }
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 285fd40..9f0051c 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise3/KirjaKokoelma.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/KirjaKokoelma.java
@@ -1,10 +1,13 @@
 package fi.utu.tech.ooj.exercise3;
 
 import java.io.PrintStream;
+import java.lang.reflect.Array;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
-public class KirjaKokoelma {
+public class KirjaKokoelma implements Cloneable {
 
     private String kokoelmanNimi;
     private  ArrayList<Kirja>kirjaListaus;
@@ -13,6 +16,49 @@ public class KirjaKokoelma {
         this.kokoelmanNimi = kokoelmanNimi;
         this.kirjaListaus = new ArrayList<>();
     }
+    @Override
+    public KirjaKokoelma clone() throws CloneNotSupportedException {
+        KirjaKokoelma k = (KirjaKokoelma)super.clone();
+        k.kirjaListaus = new ArrayList<>();
+        for (Kirja kirja : kirjaListaus) {
+            //Voimme nyt kloonata Kirja-tyyppisiä olioita, koska se ei enää ole suojattu package suojausmäärellä.
+            k.kirjaListaus.add((Kirja)kirja.clone());
+        }
+        return k;
+    }
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        for (Kirja k : getKirjaListaus()) {
+            sb.append(k.toString());
+        }
+        sb.append("]");
+        return "\nkokoelmanNimi: " + getkokoelmanNimi() + "\nkirjaListaus: " + sb;
+    }
+    @Override
+    public boolean equals(Object toinen) {
+        if (toinen == null) {
+            return false;
+        }
+        if (toinen == this) {
+            return true;
+        }
+        if (!(toinen instanceof  KirjaKokoelma)) { //toinen.getClass() != getClass()
+            return false;
+        }//Kloonataan kirjakokoelmat, jotta rutiini toimisi sivuvaikutuksettomasti
+        KirjaKokoelma k = null;
+        KirjaKokoelma t = null;
+        try {
+            k = ((KirjaKokoelma)toinen).clone();
+            t = ((KirjaKokoelma)this).clone();
+        } catch (CloneNotSupportedException e) {
+            return false;
+        }
+        Collections.sort(k.getKirjaListaus(), Comparator.comparingInt(Kirja::getJulkaisuVuosi));
+        Collections.sort(t.getKirjaListaus(), Comparator.comparingInt(Kirja::getJulkaisuVuosi));
+        return k.getkokoelmanNimi().equals(kokoelmanNimi) && t.getKirjaListaus().equals(k.getKirjaListaus());
+    }
 
     public String getkokoelmanNimi() {
         return kokoelmanNimi;
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 2478ff6..ed0ee4b 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise3/Kirjasto.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/Kirjasto.java
@@ -1,11 +1,8 @@
 package fi.utu.tech.ooj.exercise3;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
-public class Kirjasto {
+public class Kirjasto implements Cloneable {
 
     private String kirjastonNimi;
     private String osoite;
@@ -16,12 +13,68 @@ public class Kirjasto {
         this.kirjastonNimi = kirjastonNimi;
         this.osoite = osoite;
     }
+    @Override
+    public Kirjasto clone() throws CloneNotSupportedException {
+        Kirjasto k = (Kirjasto)super.clone();
+        k.kokoelmat = new ArrayList<>();
+        for (KirjaKokoelma kokoelma : kokoelmat) {
+            k.kokoelmat.add((KirjaKokoelma)kokoelma.clone());
+        }
+        k.sivukirjastot = new HashSet<>();
+        for (Kirjasto sivukirjasto : sivukirjastot) {
+            k.sivukirjastot.add((Kirjasto)sivukirjasto.clone());
+        }
+        return k;
+    }
+    @Override
+    public String toString() {
+        StringBuilder kokoelmat = new StringBuilder();
+        kokoelmat.append("");
+        for (KirjaKokoelma k : getKokoelmat()) {
+            kokoelmat.append(k.toString());
+        }
+        kokoelmat.append("");
+        StringBuilder sivukirjastot = new StringBuilder();
+        for (Kirjasto ko : getSivukirjastot()) {
+            sivukirjastot.append(ko.toString()); // toteutetaan tämä rekursiivisesti
+        }
+        return "kirjastonNimi: " + getKirjastonNimi() + "\nosoite: " + getOsoite() + "\nkokoelmat: " + kokoelmat +
+                "\nsivuKirjastot: " + Arrays.toString(getSivukirjastot().stream().map(kirjasto -> kirjasto.getKirjastonNimi()).toArray(String[]::new)) + "\n" + sivukirjastot;
+    }
+    @Override
+    public boolean equals(Object toinen) {
+        if (toinen == null) {
+            return false;
+        }
+        if (toinen == this) {
+            return true;
+        }
+        if (!(toinen instanceof  Kirjasto)) { //toinen.getClass() != getClass()
+            return false;
+        }//Kloonataan kirjastot, jotta rutiini toimisi sivuvaikutuksettomasti
+        Kirjasto k = null;
+        Kirjasto t = null;
+        try {
+            k = ((Kirjasto)toinen).clone();
+            t = ((Kirjasto)this).clone();
+        } catch (CloneNotSupportedException e) {
+            return false;
+        }
+        Collections.sort(k.getKokoelmat(), Comparator.comparing(KirjaKokoelma::getkokoelmanNimi));
+        Collections.sort(t.getKokoelmat(), Comparator.comparing(KirjaKokoelma::getkokoelmanNimi));
+
+        for (Kirjasto kirjasto:t.getSivukirjastot()) {
+            if (!new ArrayList(List.of(k.getSivukirjastot().toArray())).contains(kirjasto)) {
+                return false;
+            }
+        }
+        return k.getKirjastonNimi().equals(t.getKirjastonNimi()) && k.getOsoite().equals(t.getOsoite());
+                //&& k.getSivukirjastot().equals(t.getSivukirjastot());
+    }
 
     public Set<Kirjasto> getSivukirjastot() {
         return sivukirjastot;
     }
-
-
     public String getKirjastonNimi() {
         return kirjastonNimi;
     }
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 0c5fb2a..dd8ec7d 100644
--- a/src/main/java/fi/utu/tech/ooj/exercise3/Main.java
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/Main.java
@@ -3,6 +3,8 @@ package fi.utu.tech.ooj.exercise3;
 
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 
 public class Main {
 
@@ -19,10 +21,107 @@ public class Main {
          * Testin jälkeen alla olevan rivin voi joko kommentoida tai poistaa.
          */
         System.out.println("*** Harjoitustyöpohja käynnistyy ***");
+        Kirjasto turku = new Kirjasto("Turun pääkirjasto", "kirjatokatu 3");
+        Kirjasto nummi = new Kirjasto("nummen kirjasto", "nummenkatu 1");
+        Kirjasto itaharju = new Kirjasto("ita-harjun kirjasto", "itainen 7");
+        turku.lisaaSivukirjasto(nummi);
+        turku.lisaaSivukirjasto(itaharju);
 
-        HashSet<Ajoneuvo> ajoneuvot = new HashSet<Ajoneuvo>();
-        HashSet<HenkiloAuto> henkiloautot = new HashSet<HenkiloAuto>();
-        HashSet<KuormaAuto> kuormaautot = new HashSet<KuormaAuto>();
+        Kirja nainmuututmiljardooriksi = new Kirja("Näin muutut miljärdööriksi", "Pöhinä Petri", 2018);
+        Kirja universuminmysteetit = new Kirja("Universumin mysteerit", "Tiede Teppo", 2015);
+        KirjaKokoelma fakta = new KirjaKokoelma("Faktakirjallisuus");
+        fakta.lisaaKirja(nainmuututmiljardooriksi);
+        fakta.lisaaKirja(universuminmysteetit);
+
+        Kirja tahtientuhoja = new Kirja("Tähtien tuhoaja", "Luukas Yrjö", 1979);
+        Kirja robotittappaakaikki = new Kirja ("Robotit tappavat kaikki", "Pelkäävä Pentti", 2011);
+        KirjaKokoelma Scifi = new KirjaKokoelma("Sci-fi");
+        Scifi.lisaaKirja(tahtientuhoja);
+        Scifi.lisaaKirja(robotittappaakaikki);
+
+        Kirja fantastinenjuttu = new Kirja ("Fantastinen juttu", "Vitsaileva Viivi", 1996);
+        Kirja valtaistuinpeli = new Kirja ("Valtaistuinpeli", "Martti Yrjö", 1999);
+        KirjaKokoelma fantasia = new KirjaKokoelma("fantasia");
+        fantasia.lisaaKirja(fantastinenjuttu);
+        fantasia.lisaaKirja(valtaistuinpeli);
+
+        Kirja elamani = new Kirja("Elämäni", "Elävä Eeli", 2011);
+        Kirja menestys = new Kirja("Menestys", "Menestyvä Maija", 2012);
+        KirjaKokoelma elamankerrat = new KirjaKokoelma("Elämänkerrat");
+        elamankerrat.lisaaKirja(elamani);
+        elamankerrat.lisaaKirja(menestys);
+
+        Kirja meditointi = new Kirja("Meditointi", "Kultainen Markus", 100);
+        Kirja ajattelensiisolen = new Kirja("Ajattelen siis olen", "Tuhoisa Rene", 1600);
+        KirjaKokoelma filosofia = new KirjaKokoelma("Filosofia");
+        filosofia.lisaaKirja(ajattelensiisolen);
+        filosofia.lisaaKirja(meditointi);
+
+        Kirja jotainrunoja = new Kirja("Jotain runoja", "Runoileva Roni", 1999);
+        Kirja lisaaRunoja = new Kirja("Lisaa runoja", "Runoileva Roni", 2005);
+        KirjaKokoelma runollisuus = new KirjaKokoelma("Runollisuus");
+        runollisuus.lisaaKirja(jotainrunoja);
+        runollisuus.lisaaKirja(lisaaRunoja);
+
+        turku.lisaaKokoelma(fakta);
+        turku.lisaaKokoelma(Scifi);
+        nummi.lisaaKokoelma(fantasia);
+        nummi.lisaaKokoelma(runollisuus);
+        itaharju.lisaaKokoelma(filosofia);
+        itaharju.lisaaKokoelma(elamankerrat);
+
+        System.out.println(turku.toString());
+        Kirjasto åbo = null;
+        try {
+            åbo = turku.clone();
+        } catch (Exception e) {}
+        System.out.println(åbo.toString());
+        System.out.println(turku.equals(åbo));
+
+        HashMap<String, Integer> kaarittava = new HashMap<>();
+        kaarittava.put("esim1", 214134);
+        kaarittava.put("esimerkki2", 15);
+        kaarittava.put("esimerkillinen3", 346);
+        RandomMap<String, Integer> kaari = new RandomMap<>(kaarittava);
+        System.out.println(kaari.size());
+        System.out.println(kaari.get("e"));
+
+        Triplet<String, Integer, Integer> tripletti = new Triplet<>();
+        tripletti.put("esim1", 0, 251);
+        tripletti.put("esimerkki2", 15, 34);
+        System.out.println(tripletti.size());
+        System.out.println(tripletti.firstValues());
+        System.out.println(tripletti.secondValues());
+        System.out.println(tripletti.keySet());
+        System.out.println(tripletti.containsValue(0, 251));
+        System.out.println(tripletti.containsKey("esim1"));
+        System.out.println(tripletti.isEmpty());
+        System.out.println(tripletti.getFirstValue("esimerkki2"));
+        System.out.println(tripletti.getSecondValue("esimerkki2"));
+        tripletti.remove("esimerkki2");
+        System.out.println(tripletti.keySet());
+        tripletti.clear();
+        System.out.println(tripletti.keySet());
+
+        //Lisäys onnistuu, koska Liskovin korvausperiaatteen mukaan henkilöaudon ja kuormaaudon on käyttäytyvä, kuten
+        //ajoneuvo, jonka takia lisäyksen on oltava mahdollista. Iteratiivinen lisääminen addall metodilla on
+        //mahdollista
+        //HashSet<Ajoneuvo> ajoneuvot = new HashSet<Ajoneuvo>();
+        //HashSet<KuormaAuto> kuormaautot = new HashSet<KuormaAuto>();
+        //HashSet<HenkiloAuto> henkiloautot = new HashSet<HenkiloAuto>();
+        //ajoneuvot.addAll(henkiloautot);
+        //ajoneuvot.addAll(kuormaautot);
+        //SEt ajoneuvot luokkaa ei ole kuitenkaan mahdollista korvata luokalla, jonka tyypiargumenttina on sen aliluokka
+        //sen takia, että Javassa tämä ominaisuus on toteutettu eri tavalla. Javassa on käytettävä vapaata tyyppiä,
+        //jotta tämä olisi mahdollista. Liskovin korvausperiaatteen mukaan kuitenkin tämän pitäisi normaalistikin
+        //olla mahdollista, ja se on toteutettu eri tavoilla muissa kielissä. Javassa kuitenkin geneerisyys on
+        //käyttäytyy aina invarianttisesti
+        HashSet<? extends Ajoneuvo> ajoneuvot = new HashSet<Ajoneuvo>(); // Tämä voidaan korjata näin
+        ajoneuvot = new HashSet<HenkiloAuto>(); // jolloin tämä toimii
+        //Tämä toimii sen takia, että tyyppinä on vapaa tyyppi, joka seuraa liskovin korvausperiaatetta tarkemmin
+
+
+        System.out.println(ajoneuvot);
 
     }
 }
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 0000000..d2f8341
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/RandomMap.java
@@ -0,0 +1,89 @@
+package fi.utu.tech.ooj.exercise3;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+public class RandomMap<K, V> implements Map<K, V> {
+    private Map<K, V> kaarittava;
+
+    public RandomMap(Map<K, V> kaarittava) {
+        this.kaarittava = kaarittava;
+    }
+    @Override
+    public int size() {
+        return kaarittava.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return kaarittava.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return kaarittava.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return kaarittava.containsValue(value);
+    }
+
+    @Override
+    public V get(Object key) {
+        Iterator<Entry<K,V>> i = kaarittava.entrySet().iterator();
+        if (key==null) {
+            while (i.hasNext()) {
+                Entry<K,V> e = i.next();
+                if (e.getKey()==null)
+                    return e.getValue();
+            }
+        } else {
+            while (i.hasNext()) {
+                Entry<K,V> e = i.next();
+                if (key.equals(e.getKey()))
+                    return e.getValue();
+            }
+        }
+            List<V> a = new ArrayList<>(kaarittava.values().stream().toList());
+            Collections.shuffle(a);
+            return a.get(0);
+        }
+
+    @Override
+    public V put(K key, V value) {
+        return kaarittava.put(key, value);
+    }
+
+    @Override
+    public V remove(Object key) {
+        return kaarittava.remove(key);
+    }
+
+    @Override
+    public void putAll(Map<? extends K, ? extends V> m) {
+        kaarittava.putAll(m);
+    }
+
+    @Override
+    public void clear() {
+        kaarittava.clear();
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return kaarittava.keySet();
+    }
+
+    @Override
+    public Collection<V> values() {
+        return kaarittava.values();
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet() {
+        return kaarittava.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 0000000..b407e64
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/Triplet.java
@@ -0,0 +1,169 @@
+package fi.utu.tech.ooj.exercise3;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class Triplet<K, V, W> implements TripletMap<K, V, W> {
+
+    private ArrayList<Entry<K, V, W>> table;
+    private Set<K> keys;
+    private Set<V> firstValues;
+    private Set<W> secondValues;
+    private int size;
+
+
+    static class Node<K, V, W> implements TripletMap.Entry<K, V, W> {
+        final K key;
+        V firstValue;
+        W secondValue;
+
+        public Node(K key, V firstValue, W secondValue) {
+            this.key = key;
+            this.firstValue = firstValue;
+            this.secondValue = secondValue;
+        }
+
+        @Override
+        public K getKey() {
+            return key;
+        }
+
+        @Override
+        public V getFirstValue() {
+            return firstValue;
+        }
+
+        @Override
+        public V setFirstValue(V firstValue) {
+            this.firstValue = firstValue;
+            return firstValue;
+        }
+
+        @Override
+        public W getSecondValue() {
+            return secondValue;
+        }
+
+        @Override
+        public W setSecondValue(W secondValue) {
+            this.secondValue = secondValue;
+            return secondValue;
+        }
+    }
+
+    public Triplet() {
+        table = new ArrayList<Entry<K, V, W>>();
+        keys = new HashSet<>();
+        firstValues = new HashSet<>();
+        secondValues = new HashSet<>();
+    }
+
+    @Override
+    public void put(K key, V value1, W value2) throws IllegalArgumentException {
+        if (! containsKey(key) || firstValues.contains(value1) || secondValues.contains(value2)) {
+            table.add(new Node<>(key, value1, value2));
+            keys.add(key);
+            firstValues.add(value1);
+            secondValues.add(value2);
+            size++;
+        } else {
+        }
+    }
+
+    @Override
+    public V getFirstValue(K key) {
+        int i = 0;
+        while (table.size() - i != 0) {
+            if (table.get(i).getKey().equals(key)) {
+                return table.get(i).getFirstValue();
+            } else {
+                i++;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public W getSecondValue(K key) {
+        int i = 0;
+        while (table.size() - i != 0) {
+            if (table.get(i).getKey().equals(key)) {
+                return table.get(i).getSecondValue();
+            } else {
+                i++;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void remove(K key) {
+        int i = 0;
+        while (table.size() - i != 0) {
+            if (table.get(i).getKey().equals(key)) {
+                table.remove(i);
+                keys.remove(key);
+                firstValues.remove(getFirstValue(key));
+                secondValues.remove(getSecondValue(key));
+                size--;
+            } else {
+                i++;
+            }
+        }
+    }
+
+    @Override
+    public void clear() {
+        table.clear();
+        keys.clear();
+        firstValues.clear();
+        secondValues.clear();
+        size = 0;
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return keys;
+    }
+
+    @Override
+    public Set<V> firstValues() {
+        return firstValues;
+    }
+
+    @Override
+    public Set<W> secondValues() {
+        return secondValues;
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return keySet().contains(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value1, Object value2) {
+        int i = 0;
+        while (table.size() - i != 0) {
+            if (table.get(i).getFirstValue().equals(value1) && table.get(i).getSecondValue().equals(value2)) {
+                return true;
+            } else {
+                i++;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return table.isEmpty();
+    }
+
+    @Override
+    public int size() {
+        return 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 0000000..f8ab807
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/TripletMap.java
@@ -0,0 +1,39 @@
+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();
+    //Luodaan yksittäisille arvoille oma luokka, jotta arvojen käsittely olisi yksinkertaisempaa
+    interface Entry<K, V, W> {
+        K getKey();
+        V getFirstValue();
+        W getSecondValue();
+        V setFirstValue(V value);
+        W setSecondValue(W value);
+    }
+}
diff --git a/src/main/java/fi/utu/tech/ooj/exercise3/tehtava1.txt b/src/main/java/fi/utu/tech/ooj/exercise3/tehtava1.txt
new file mode 100644
index 0000000..c6549ce
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/tehtava1.txt
@@ -0,0 +1,30 @@
+a. Collection:ia voidaan ajatella olevan joukko arvoja. Map toisaalta on joukko avain-arvo pareja, jossa avain kertoo
+arvon sijannin. Collection:in perijöitä ovat List, Set ja Queue. Listassa voi olla duplikaatti arvoja, mutta Set:issä
+ei. Map:in avaimia voidaan tällöin ajatella olevan samankaltaisia kuin Set ja arvojen kuten List, koska Map:issä ei
+voi olla kaksi saman nimistä avainta. Map:ia kannattaa käyttää, kun on hyödyllistä yhdistää tieto tiettyyn arvoon. Eikä
+tieto ole dynaaminen, mikä voi sekoittaa Map:in toimintoa. Map:in läpi tällöin ei tarvitse iteroida ja tieto
+voidaan löytää suoraan avaimella. Esimerkki tästä olisi opiskelijan etsintä opiskelijanumeroilla.
+Collectionia toisaalta kannattaa käyttää, kun on sallittavaa iteroida sen läpi löytääkseen tietyn arvon.
+b.
+Set eroaa List:istä ja queue:stä siten, että set:issä järjestys ei ole yhtä oleellinen. Set:in kaikki arvot on oltava
+erit, jotta tietorakenteesta voidaan löytää tieto. Set on tällöin tapa varmistaa, että joka ikinen arvo on eri. Se olisi
+esimerkiksi hyvä tapa tarkastaa, että on onko henkiötunnus validi etsimällä sen tietokannasta. List on toisaalta
+parempi rakenne, jos halutaan sallia duplikaatit ja järjestys. Esimerkiksi kirja-olioiden lajittelu kirjalijan nimen
+mukaan. Queue on näistä ankarin, se vaatii, että alkioiden poisto ja lisäys tapahtuu vain tietyssä järjestyksessä.
+Ei ole mahdollista poistaa jäsentä Queue:n keskeltä esimerkiksi. Queue:llä voidaan esimerkiksi mallintaa
+TYS:in asuntojen odottajia prioriteettijonona. Nykyisen asunnon etäisyys Turusta tai vaihto-opiskelijaisuus vaikuttaisi
+mahdollisuuteen saada asunto, jolloin odottaja voidaan poistaa listasta.
+c.
+LinkedList:in on tuplasti linkitetty lista, joka tarkoittaa, että alkioissa on tieto edeltävästä ja seuraavasta
+alkioista. LinkedList ja ArrayList ovat mutatoituvia. LinkedList on kuitenkin nopeampi, kun vaaditaan paljon jäsenien
+lisäystä ja poistoa. ArrayList on parempi, jos on tärkeää löytää tietty jäsen listasta. ArrayList on toteutettu
+Array rakenteella. ArrayList on hyvä yleiskäyttöinen tietorakenne. Sitä voidaan käyttää, kun listasta halutaan tehdä
+mutatoituva. Esimerkiksi ostoslista, jossa halutaan sallia duplikaatit ja mutatoitavuus. LinkedList kannattaa valita,
+kun suoritusresurssit ovat vähäisiä, ja halutaan lisätä tietorakenne, joka jatkuvasti lisää ja poistaa alkoita.
+Esimerkisi lista tietyn kryptovaluutan omistajista.
+d.
+HashSet:in ja TreeSet:in pääero on, että HashSet ei takaa järjestystä. Tämän lisäksi HashMap sallii Null-arvot.
+HashSet:in perusoperaatiot ovat suoritusnopeudeltaan vakioita. TreeSet:in perusoperaatiot ovat toisaalta logaritmisia.
+HashSet:iä kannattaa käyttää, kun järjestys on epäoleellinen. Esimerkiksi rutiini, joka tarkastaa onko
+sähköpostiosoitetta tietorakenteessa. TreeSet on hyvä vaihtoehto, kun halutaan tietorakenteella olevan järjestys, mutta
+silti ei sallita duplikaatteja. Esimerkiksi grafiiikkakorttien järjestäminen prosessointivoimansa mukaisesti.
\ No newline at end of file
-- 
GitLab