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 6ac23592a2e0dc1574161b4d6600de263c61ced2..20bac3e3a3d06ebcd9644cbdf795fb2e0942607e 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.LinkedHashMap;
+import java.util.Map;
 
 public class Main {
 
@@ -21,6 +23,9 @@ public class Main {
         System.out.println("*** Harjoitustyöpohja käynnistyy ***");
 
 
+
+
+        //Tehtävä 2 demonstraatio
         Kirjasto paakirjasto = new Kirjasto("Turun pääkirjasto", "Turku");
         Kirjasto sivukirjasto1 = new Kirjasto("Sivukirjasto 1", "Turku");
         Kirjasto sivukirjasto2 = new Kirjasto("Sivukirjasto 2", "Turku");
@@ -65,6 +70,23 @@ public class Main {
         //Kun alla oleva rivi suoritetaan, niin huomataan, että oliot eivät ole samat. Tämä johtuu siitä, että näiden olioiden muistipaikat ovat eriävät ja eivät ole tästä syystä samat.
         System.out.println("Alkuperäinen kirjasto ja sen kopio ovat samat: " + paakirjasto.equals(kopio));
 
+
+        //Tehtävä 3 demonstraatio
+        Map<String, String> originalMap = new LinkedHashMap<>();
+        originalMap.put("Avain1", "Arvo1");
+        originalMap.put("Avain2", "Arvo2");
+        originalMap.put("Avain3", "Arvo3");
+
+        Map<String, String> randomMap = new RandomMap<>(originalMap);
+
+        String value1 = randomMap.get("Avain1");
+        System.out.println("Avaimen 1 arvo: " + value1);
+
+        String value4 = randomMap.get("Avain4");
+        System.out.println("Avaimen 4 arvo (satunnainen): " + value4);
+
+
+
         // HashSet<Ajoneuvo> ajoneuvot = new HashSet<Ajoneuvo>();
         // HashSet<HenkiloAuto> henkiloautot = new HashSet<HenkiloAuto>();
         // HashSet<KuormaAuto> kuormaautot = new HashSet<KuormaAuto>();
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..12ada06518ae298c70e63510b75d8a959286f9ed
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/RandomMap.java
@@ -0,0 +1,83 @@
+package fi.utu.tech.ooj.exercise3;
+
+import java.util.*;
+
+public class RandomMap<K, V> implements Map<K, V> {
+    private final Map<K, V> wrappedMap;
+    private final Random random;
+
+    public RandomMap(Map<K, V> map) {
+        this.wrappedMap = map;
+        this.random = new Random();
+    }
+
+    @Override
+    public int size() {
+        return wrappedMap.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return wrappedMap.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return wrappedMap.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return wrappedMap.containsValue(value);
+    }
+
+    @Override
+    public V get(Object key) {
+        if (wrappedMap.containsKey(key)) {
+            return wrappedMap.get(key);
+        } else {
+            List<V> values = new ArrayList<>(wrappedMap.values());
+            if (!values.isEmpty()) {
+                int randomIndex = random.nextInt(values.size());
+                return values.get(randomIndex);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    @Override
+    public V put(K key, V value) {
+        return wrappedMap.put(key, value);
+    }
+
+    @Override
+    public V remove(Object key) {
+        return wrappedMap.remove(key);
+    }
+
+    @Override
+    public void putAll(Map<? extends K, ? extends V> m) {
+        wrappedMap.putAll(m);
+    }
+
+    @Override
+    public void clear() {
+        wrappedMap.clear();
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return wrappedMap.keySet();
+    }
+
+    @Override
+    public Collection<V> values() {
+        return wrappedMap.values();
+    }
+
+    @Override
+    public Set<Map.Entry<K, V>> entrySet() {
+        return wrappedMap.entrySet();
+    }
+}
\ No newline at end of file
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..29b8d36837917d3fcb5ed527bc443d60b93301e0
--- /dev/null
+++ b/src/main/java/fi/utu/tech/ooj/exercise3/Triplet.java
@@ -0,0 +1,130 @@
+package fi.utu.tech.ooj.exercise3;
+
+import java.util.Set;
+
+/*
+ * Triplet luokka ottaa kolme eri tyyppiparametria, jotka ovat K (avaintyyppi), V1 (ensimmäinen arvon tyyppi) sekä V2 (toisen arvon tyyppi)
+ * Luokalle on myös määritelty kolme luokkamuuttujaa, jotka ovat key, value1, value2
+ */
+
+public class Triplet<K, V1, V2> implements TripletMap<K, V1, V2> {
+    private K key;
+    private V1 value1;
+    private V2 value2;
+
+    //Konstruktori alustaa tyhjän Triplet-olion
+    public Triplet() {
+        this.key = null;
+        this.value1 = null;
+        this.value2 = null;
+    }
+
+    public Triplet(K key, V1 value1, V2 value2) {
+        this.key = key;
+        this.value1 = value1;
+        this.value2 = value2;
+    }
+
+    //Luodaan getterit ja setterit Triplet-luokalle.
+    
+    public K getKey() {
+        return key;
+    }
+
+    public void setKey(K key) {
+        this.key = key;
+    }
+
+    public V1 getValue1() {
+        return value1;
+    }
+
+    public void setValue1(V1 value1) {
+        this.value1 = value1;
+    }
+
+    public V2 getValue2() {
+        return value2;
+    }
+
+    public void setValue2(V2 value2) {
+        this.value2 = value2;
+    }
+
+    //Käytetään rajapinnan TripletMap tarjoamia toimintoja.
+
+    @Override
+    public void put(K key, V1 value1, V2 value2) {
+        this.key = key;
+        this.value1 = value1;
+        this.value2 = value2;
+    }
+
+    @Override
+    public V1 getFirstValue(K key) {
+        if (this.key.equals(key)) {
+            return value1;
+        }
+        return null;
+    }
+
+    @Override
+    public V2 getSecondValue(K key) {
+        if (this.key.equals(key)) {
+            return value2;
+        }
+        return null;
+    }
+
+    @Override
+    public void remove(K key) {
+        if (this.key.equals(key)) {
+            this.key = null;
+            this.value1 = null;
+            this.value2 = null;
+        }
+    }
+
+    @Override
+    public void clear() {
+        this.key = null;
+        this.value1 = null;
+        this.value2 = null;
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return (key != null) ? Set.of(key) : Set.of();
+    }
+
+    @Override
+    public Set<V1> firstValues() {
+        return (value1 != null) ? Set.of(value1) : Set.of();
+    }
+
+    @Override
+    public Set<V2> secondValues() {
+        return (value2 != null) ? Set.of(value2) : Set.of();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return (this.key != null && this.key.equals(key));
+    }
+
+    @Override
+    public boolean containsValue(Object value1, Object value2) {
+        return (this.value1 != null && this.value1.equals(value1) && this.value2 != null && this.value2.equals(value2));
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return (key == null && value1 == null && value2 == null);
+    }
+
+    @Override
+    public int size() {
+        return (key != null) ? 1 : 0;
+    }
+
+}
\ No newline at end of file
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..c2d9c7e59227332619897548aa804229a7426b2e
--- /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> {
+    //Ryhmä 1:
+    void put(K key, V value1, W value2);
+    V getFirstValue(K key);
+    W getSecondValue(K key);
+    void remove(K key);
+    void clear();
+
+    //Ryhmä 2:
+    Set<K> keySet();
+    Set<V> firstValues();
+    Set<W> secondValues();
+    boolean containsKey(Object key);
+    boolean containsValue(Object value1, Object value2);
+    boolean isEmpty();
+    int size();
+}
\ No newline at end of file