Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Java Generics and Collection classes

Java Generics and Collection classes

Java Generics and Collection classes

Jussi Pohjolainen

October 10, 2016
Tweet

More Decks by Jussi Pohjolainen

Other Decks in Technology

Transcript

  1. class Box<T> { private T object; public void set(T object)

    { this.object = object; } public T get() { return object; } public static void main(String[] args) { Box<String> b = new Box<String>(); b.set("moi"); System.out.println(b.get()); } } Type that we can determine runtime It is a String now
  2. class Box<T> { private T object; public void set(T object)

    { this.object = object; } public T get() { return object; } public static void main(String[] args) { // Java SE 7 Box<String> b = new Box<>(); b.set("moi"); System.out.println(b.get()); } } Shorter syntax in Java 7
  3. class Box<T> { private T object; public void set(T object)

    { this.object = object; } public T get() { return object; } public static void main(String[] args) { // Java SE 10 var b = new Box<String>(); b.set("moi"); System.out.println(b.get()); } } Shorter syntax in Java 10
  4. class Pair<K, V> { private K key; private V value;

    public static void main(String[] args) { Pair<String, Integer> b = new Pair<>(); b.key = "key"; b.value = new Integer(3); // or by using autoboxing // = 3; } } Two generic values In Java 5 autoboxing makes automatic conversion
  5. class Test { public static void main(String[] args) { String

    result1 = Test.<String>doSomething("Hello"); int result2 = Test.<Integer>doSomething(5); double result3 = Test.doSomething(3.3); float result4 = doSomething(3.3f); } public static <T> T doSomething(T element) { return element; } } Generic method Possibility to omit the <X>
  6. class Test { public static void main(String[] args) { int

    result2 = Test.<Integer>doSomething(5); double result3 = Test.doSomething(3.3); float result4 = doSomething(3.3f); } public static <T extends Number> int doSomething(T element) { return element.intValue(); } } Using extends Can use Number methods
  7. @FunctionalInterface interface Flyable { public void fly(); } class Bird

    implements Flyable { public void fly() { System.out.println("bird in the air!"); } } class Airplane implements Flyable { public void fly() { System.out.println("airplane in the air!"); } } class Test { public static void main(String[] args) { doSomething(new Bird()); doSomething(new Airplane()); doSomething(new Flyable() { @Override public void fly() { System.out.println("Anonymous inner class in the air"); } }); doSomething(() -> System.out.println("Lambda in the air")); } public static <T extends Flyable> void doSomething(T stuff) { stuff.fly(); } } extends?
  8. class Box<T> { T value; } class Test { public

    static void main(String[] args) { Box<String> b1 = new Box<>(); Box<String> b2 = new Box<>(); doSomething(b1, b2); } public static <T> void doSomething(Box<T> b1, Box<T> b2) { } } Generic method
  9. class Box<T> { T value; } class Test { public

    static void main(String[] args) { Box<String> b1 = new Box<>(); Box<Integer> b2 = new Box<>(); doSomething(b1, b2); } public static <T> void doSomething(Box<T> b1, Box<T> b2) { } } Won't work!
  10. class Box<T> { T value; } class Test { public

    static void main(String[] args) { Box<String> b1 = new Box<>(); Box<Integer> b2 = new Box<>(); doSomething(b1, b2); } public static <T1, T2> void doSomething(Box<T1> b1, Box<T2> b2) { } } Now it works!
  11. class Box<T> { T value; } class Test { public

    static void main(String[] args) { Box<String> b1 = new Box<>(); b1.value = "hello"; Box<Integer> b2 = new Box<>(); b2.value = 5; // new Integer(5) doSomething(b1, b2); } public static <T1, T2> void doSomething(Box<T1> b1, Box<T2> b2) { T1 value1 = b1.value; T2 value2 = b2.value; System.out.println(value1.toString()); System.out.println(value2.toString()); } } Is toString() accessible?
  12. class Box<T> { T value; } class Test { public

    static void main(String[] args) { Box<String> b1 = new Box<>(); b1.value = "hello"; Box<Integer> b2 = new Box<>(); b2.value = 5; // new Integer(5) doSomething(b1, b2); } public static <T1, T2> void doSomething(Box<T1> b1, Box<T2> b2) { T1 value1 = b1.value; T2 value2 = b2.value; System.out.println(value1.charAt(0)); } } Is charAt() accessible?
  13. class Box<T> { T value; } class Test { public

    static void main(String[] args) { Box<String> b1 = new Box<>(); b1.value = "hello"; Box<Integer> b2 = new Box<>(); b2.value = 5; // new Integer(5) doSomething(b1, b2); } public static <T1, T2> void doSomething(Box<T1> b1, Box<T2> b2) { T1 value1 = b1.value; T2 value2 = b2.value; System.out.println(value1 + value2); } } Can we use + - operator?
  14. class Box<T extends Number> { T value; } class Test

    { public static void main(String[] args) { Box<Integer> b1 = new Box<>(); b1.value = 5; // new Integer(5) Box<Integer> b2 = new Box<>(); b2.value = 5; // new Integer(5) doSomething(b1, b2); } public static <T1 extends Number, T2 extends Number> void doSomething(Box<T1> b1, Box<T2> b2) { T1 value1 = b1.value; T2 value2 = b2.value; System.out.println(value1.intValue() + value2.intValue()); } } Must extend Number intValue now works
  15. class Box<T extends Runnable> { public T value; public void

    startThread() { new Thread(value).start(); } } class Test implements Runnable { public static void main(String[] args) { Box<Test> b1 = new Box<>(); b1.value = new Test(); b1.startThread(); } public void run() { System.out.println("Hello from thread"); } } Does it work?
  16. interface CrudRepository<T, ID> { public void add(T element); public T

    get(ID id); } interface PersonRepository extends CrudRepository<Person, Long> { } Reusing base interface
  17. import java.util.List; class Test { public static void main(String[] args)

    { List<Integer> list1 = List.of(1,2,3,4,5); List<Integer> list2 = List.of(1,2,3,4,5); Test.<Integer>copy(list1, list2); } public static <T extends Number> void copy(List<T> dest, List<T> src) { // copy src to dest } } since java 9
  18. import java.util.List; class Test { public static void main(String[] args)

    { List<Integer> list1 = List.of(1, 2, 3, 4, 5); List<Double> list2 = List.of(1.1, 2.2, 3.3, 4.4, 5.5); Test.<Integer>copy(list1, list2); } public static <T extends Number> void copy(List<T> dest, List<T> src) { // copy src to dest } } Does it work?
  19. import java.util.List; class Test { public static void main(String[] args)

    { List<Integer> list1 = List.of(1,2,3,4,5); List<Double> list2 = List.of(1.1,2.2,3.3,4.4,5.5); Test.<Integer, Double>copy(list1, list2); } public static <T1 extends Number, T2 extends Number> void copy(List<T1> dest, List<T2> src) { // copy src to dest } } How about now?
  20. import java.util.List; class Test { public static void main(String[] args)

    { List<Integer> list1 = List.of(1,2,3,4,5); List<Double> list2 = List.of(1.1,2.2,3.3,4.4,5.5); Test.<Integer, Double>copy(list1, list2); } public static void copy(List<? extends Number> dest, List<? extends Number> src) { // copy src to dest } } Does the same but maybe easier syntax?
  21. import java.util.List; class Person {} class Programmer extends Person {}

    class Waitress extends Person {} class Test { public static void main(String[] args) { var list1 = List.of(new Programmer(), new Programmer()); var list2 = List.of(new Waitress(), new Waitress()); Test.<Programmer, Waitress>copy(list1, list2); } public static void copy(List<? extends Person> dest, List<? extends Person> src) { } } Using Custom Classes
  22. import java.util.*; class Person {} class Programmer extends Person {}

    class Waitress extends Person {} class Test { public static void main(String[] args) { var list1 = List.of(new Person(), new Programmer()); var list2 = List.of(new Person(), new Waitress(), new Object()); Test.<Programmer, Waitress>copy(list1, list2); } public static void copy(List<? super Programmer> dest, List<? super Waitress> src) { } } Can be Waitress or super class
  23. class Person {} class Programmer extends Person {} class Waitress

    extends Person {} class Test { public static void main(String[] args) { var list1 = List.of(new Programmer(), new Programmer()); var list2 = List.of(new Person(), new Person()); Test.<Programmer, Waitress>copy(list1, list2); } public static <T1 super Programmer, T2 super Waitress> void copy(List<T1> dest, List<T2> src) { } } Super is only supported in the wild card.., does not work
  24. Collection Interface • Basic parts of almost all collections •

    Can be iterated, because it's an subinterface of Iterable • add() • remove() • toArray() • contains() • Collection - interface is generic
  25. Main Types • List • May contain duplicates, like an

    array • Set • Like List, but cannot contain duplicate • Queue • Designed so that new items are added end of the queue and removed from the beginning
  26. Lists • List interface has two concrete classes • ArrayList

    – implementation uses dynamically resized arrays. Grow 50% of it's size each time • LinkedList – double-linked list. Also implements Queue – interface. • Also Vector (deprecated) • Performance varies depending on what you do!
  27. import java.util.*; record Dog(int id) {} class Main { public

    static void main(String [] args) { addAndIterate(new ArrayList<Dog>()); addAndIterate(new LinkedList<Dog>()); } public static void addAndIterate(List<Dog> list) { list.add(new Dog(1)); list.add(new Dog(2)); list.add(new Dog(3)); list.forEach(System.out::println); } }
  28. import java.util.*; record Dog(int id) {} class Main { public

    static void main(String [] args) { addAndIterate(new ArrayList<Dog>()); addAndIterate(new LinkedList<Dog>()); } public static void addAndIterate(Collection<Dog> list) { list.add(new Dog(1)); list.add(new Dog(2)); list.add(new Dog(3)); list.forEach(System.out::println); } } It’s also a collection
  29. Set • No duplicates! • HashSet (implements Set) • Elements

    are not ordered • LinkedHashSet (implements Set) • Provides order • TreeSet (implements SortedSet) • Elements are sorted
  30. import java.util.*; record Dog(int id) {} class Main { public

    static void main(String [] args) { addAndIterate(new HashSet<Dog>()); addAndIterate(new LinkedHashSet<Dog>()); // addAndIterate(new TreeSet<Dog>()); } public static void addAndIterate(Set<Dog> list) { list.add(new Dog(1)); list.add(new Dog(2)); list.add(new Dog(3)); list.forEach(System.out::println); } } On’t work, how to put these into order?
  31. import java.util.*; record Dog(int id) implements Comparable<Dog> { @Override public

    int compareTo(final Dog dog) { return dog.id - this.id; } } class Main { public static void main(String [] args) { addAndIterate(new HashSet<Dog>()); addAndIterate(new LinkedHashSet<Dog>()); addAndIterate(new TreeSet<Dog>()); } public static void addAndIterate(Set<Dog> list) { list.add(new Dog(1)); list.add(new Dog(2)); list.add(new Dog(3)); list.forEach(System.out::println); } } Works!
  32. Array Creation • Java doesn't allow the direct creation of

    a generic array of T • T [] data = new T[3] • Generics are implemented using “type erasure” • generic type information is not available at runtime • only used at compile time for type checking and then erased • This allows generics to be backward compatible with older Java versions that do not have generics • When an array is instantiated, its actual type must be known at runtime • … but generic type information is not available at runtime.
  33. public class MyArrayList { private int[] elements; private int size;

    public MyArrayList() { this.elements = new int[10]; this.size = 0; } public void add(int element) { if (size >= elements.length) { int[] newElements = new int[elements.length * 2]; System.arraycopy(elements, 0, newElements, 0, elements.length); elements = newElements; } elements[size++] = element; } public Integer get(int index) { if (index >= 0 && index < size) { return elements[index]; } return null; } }
  34. class Node { int data; Node next; public Node(int data)

    { this.data = data; this.next = null; } } class MyLinkedList { private Node head; private int size; public MyLinkedList() { this.head = null; this.size = 0; } public void add(int data) { Node newNode = new Node(data); if (head == null) { head = newNode; } else { Node current = head; while (current.next != null) { current = current.next; } current.next = newNode; } size++; } public Integer get(int index) { if (index < 0 || index >= size) { return null; } Node current = head; for (int i = 0; i < index; i++) { current = current.next; } return current.data; } }
  35. Maps • Key value pairs • HashMap • Unordered, not

    thread safe • Hashtable • Unordered, thread safe • LinkedHashMap • Ordered, thread safe • TreeMap • Sorted by keys • Not thread safe
  36. import java.util.*; record Dog(int id) implements Comparable<Dog> { @Override public

    int compareTo(final Dog dog) { return dog.id - this.id; } } class Main { public static void main(String [] args) { // unordered addAndIterate(new HashMap<String, Dog>()); // unordered addAndIterate(new Hashtable<String, Dog>()); // ordered addAndIterate(new LinkedHashMap<String, Dog>()); // sorted by keys addAndIterate(new TreeMap<String, Dog>()); } public static void addAndIterate(Map<String, Dog> map) { System.out.println(map.getClass()); map.put("key3", new Dog(3)); map.put("key1", new Dog(1)); map.put("key2", new Dog(3)); map.entrySet().stream() .forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue())); } }
  37. Hash? • Hash function takes input and turns it into

    a number • Simple example • ”jack” => [106, 97, 99, 107] => 409 • Object could be stored internally in an array in index 409 • Hash collisions could occur ”kcaj” => 409 • If this is the case, many times equals methods is used • Not having proper hashCode can also seem to work, but performance can be an issue • Many collection classes rely that hashCode (and equals) is implemented correctly • HashMap, HashSet, Hashtable, LinkedHashMap
  38. import java.util.HashMap; record GoodPerson(String name, int age) {} record BadPerson(String

    name, int age) { @Override public int hashCode() { // Poor implementation: constant hash code return 1; } @Override public boolean equals(Object o) { return this == o; // just checks memory addresses } } public class Main { public static void main(String[] args) { HashMap<GoodPerson, String> goodMap = new HashMap<>(); HashMap<BadPerson, String> badMap = new HashMap<>(); // Populate GoodPerson HashMap goodMap.put(new GoodPerson("John", 30), "Developer"); goodMap.put(new GoodPerson("Jane", 25), "Designer"); System.out.println("GoodPerson map retrieval: " + goodMap.get(new GoodPerson("John", 30))); // Populate BadPerson HashMap badMap.put(new BadPerson("John", 30), "Developer"); badMap.put(new BadPerson("Jane", 25), "Designer"); // calculates the hashcode, it is 1 // uses hashcode to find the person from an array // All instances are in index 1! // array contains a linked list which will hold two BadPersons // in index 1 // it goes through the linked list and checks with equals can // it find the given person // since equals checks memory addresses the given BadPerson object // has different address than the ones in the map. // no objects are found and null is given System.out.println("BadPerson map retrieval: " + badMap.get(new BadPerson("John", 30))); // The retrieval for BadPerson may not work as intended due to the poor equals method. } }
  39. import java.util.HashSet; record GoodPerson(String name, int age) {} record BadPerson(String

    name, int age) { @Override public int hashCode() { return 1; // Intentional collision } } class Main { public static void main(String[] args) { final int size = 10000; HashSet<GoodPerson> goodSet = new HashSet<>(); HashSet<BadPerson> badSet = new HashSet<>(); long startTime = System.nanoTime(); for (int i = 0; i < size; i++) { goodSet.add(new GoodPerson("Person", i)); } long endTime = System.nanoTime(); System.out.println("GoodPerson HashSet time: " + (endTime - startTime) + " ns"); startTime = System.nanoTime(); for (int i = 0; i < size; i++) { badSet.add(new BadPerson("Person", i)); } endTime = System.nanoTime(); System.out.println("BadPerson HashSet time: " + (endTime - startTime) + " ns"); } }