0% found this document useful (0 votes)
48 views48 pages

w05 Collections Curs

This document discusses Java data structures including collections and maps. It explains that collections hold elements of the same type while maps hold key-value pairs. It then describes common collection interfaces like List, Set, and their implementations like ArrayList, LinkedList, HashSet. It also covers the Map interface and HashMap implementation for storing key-value pairs.

Uploaded by

Cosmin Grosu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
48 views48 pages

w05 Collections Curs

This document discusses Java data structures including collections and maps. It explains that collections hold elements of the same type while maps hold key-value pairs. It then describes common collection interfaces like List, Set, and their implementations like ArrayList, LinkedList, HashSet. It also covers the Map interface and HashMap implementation for storing key-value pairs.

Uploaded by

Cosmin Grosu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 48

JAVA Development

Collections
Java Data Structures

Split into two hierarchies:

● Collections: hold elements of the same type


“Ana”, “John”, “Mary”

● Maps: hold elements accessible by a key


(key-value pairs, a dictionary)
“galaxy8” -> new Phone(“Samsung S8”, 3000),
“nokia8” -> new Phone(“Nokia 8”, 2500”)

“PI” -> 3.14159,


“e” -> 2.71828
Collections
Collection

interface Collection<E> extends Iterable<E>

- represents a generic collection, holding multiple elements of the same type

- base interface, declares some common methods:


● add(E), addAll(Collection<? extends E>)
● remove(Object), removeAll(Collection<?>), retainAll(...)
● contains(Object), containsAll(Collection<?>)
● size(), isEmpty(), clear(), toArray() ...

- no restrictions/guarantees regarding:
● order (element position)
● uniqueness
● sorting
List

interface List<E> extends Collection<E>


● An ordered collection: keeps insertion order, allows index-based access
● No uniqueness constraints - allows duplicates
● Adds specific methods:
○ get(int index): like arrays, can get elements by index
○ set(int index, E e): update element at a specific index, overwriting the old one
○ add(int index, E e): insert an element at a specific index
○ remove(int index): remove element at index
○ indexOf(Object), lastIndexOf(Object): find the index of first/last occurence of elem
○ subList(int fromIndex, int toIndex): returns part of the list
● Also inherits all methods of Collection, but declares more specific meaning for some
(considering the new properties like ordering): add(E) - adds element at end of the list, etc..
List - Implementations

Most commonly used implementations:

ArrayList<E>
● fast access by index
● slow add/remove operations
● backed by an array, needs a contiguous
memory area

LinkedList<E>
● slow access by index
● fast add/remove operations
● no need for contiguous memory area
● needs slightly more memory
List - Example

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


names.add("john");
names.add("mary");
names.add("anna");
names.add("mary"); //duplicates are allowed, so this will be added

System.out.println(names.size()); //4
System.out.println(names); //[john, mary, anna, mary] - nice toString

System.out.println(names.get(1)); //mary - index-based access, starts at 0

//accessing all elements - may use index:


for (int i = 0; i < names.size(); i++) System.out.println(names.get(i));
//also possible with simplified for:
for (String name : names) System.out.println(name);
List - Removal

List<String> names = new LinkedList<>(); //different impl

names.addAll(Arrays.asList("john", "mary", "anna", "mary"));


System.out.println(names); //[john, mary, anna, mary]

names.remove(0); //remove elem at index 0 (‘john’)

names.remove("mary"); //removes only the first occurrence!


System.out.println(names.contains("mary")); //true
System.out.println(names); //[anna, mary]

names.remove("mary"); //removes remaining occurrence


System.out.println(names.contains("mary")); //false now
Set

interface Set<E> extends Collection<E>

● A collection with unique elements (like sets in mathematics)


Set<Integer> ints = new HashSet<>();
ints.add(1);
ints.add(2);
ints.add(2); //duplicate, will not be added!
System.out.println(ints.toString()); //-> [1, 2]

● No ordering: not required to preserve insertion ordering (so elements may


seem “randomly ordered”); also no possibility for index-based access

● No extra methods compared to Collection<E>


Set - Implementations

Most commonly used implementations:

HashSet<E>
● Safest option, good performance
● Iteration order can be random
● Uses a HashMap internally (details to follow)

LinkedHashSet<E>
● In addition to a normal HashSet, also stores the elements in a double linked list
● Preserves insertion order (not required by Set interface, but happens due to the
actual implementation)
Set - HashSet example

Set<String> names = new HashSet<>();


names.add("john");
names.add("mary");
names.add("anna");
names.add("mary"); //will not be added! (duplicate, not allowed)

System.out.println(names); //[mary, john, anna] - undefined/random order


System.out.println(names.size()); //3

//names.get(1); //compile error -> no index-based access


System.out.println(names.contains("mary")); //true

//iteration possible with simplified for (in same random order)


for (String name : names) System.out.println(name); //mary john anna
Set - LinkedHashSet example

Set<String> names = new LinkedHashSet<>();


names.add("john");
names.add("mary");
names.add("anna");
names.add("mary"); //will not be added! (duplicate, not allowed)

System.out.println(names); //[john, mary, anna] - keeps insertion order

for (String name : names) System.out.println(name); //john mary anna


Set - Other implementations

TreeSet<E>
● elements are sorted (don’t keep insertion order, but are ordered by a specific
criteria - using either compareTo or with a custom Comparator)
● specific methods:
■ first, last
■ floor: find the greatest element lower or equal to the given argument
■ ceiling: find the lowest element greater or equal to the given argument
■ higher, lower: greatest/lowest elem strictly lower/greater than given arg
■ subSet, tailSet, headSet, descendingSet, pollFirst, pollLast, etc.

See docs at: https://docs.oracle.com/javase/8/docs/api/java/util/TreeSet.html


Set - TreeSet example

Set<String> names = new TreeSet<>();

names.add("john");
names.add("mary");
names.add("anna");
names.add("mary"); //duplicate (so not added)
names.add("dan");
names.add("emma");

System.out.println(names); //[anna, dan, emma, john, mary] - sorted alphabetically!


Collections - Common implementations
Collections - Choosing the right one

- Most commonly used:


ArrayList , HashSet

- You need a good reason to


pick something else...

HashSet
TreeSet
Collection - Other types

● Stack: last in, first out

● Queue: first in, first out


○ ArrayQueue

● Deque: operations at both ends (“double ended queue”)


○ ArrayDeque, LinkedList

● PriorityQueue: a queue in which elements have a priority


○ elements are not sorted
○ only interested in getting the element with the highest priority
○ e.g.: support tickets, E.R. patients
Collections - Utility class

java.lang.Collections - class with some static utility methods you may use/explore:
- sort - sort a collection by natural ordering of the elements
- binarySearch - searches for an element in a sorted collection (fast)
- min, max - find min/max element of a collection (according to their natural ordering)
- replaceAll - replace all occurrences of a specific element
- reverse, rotate, shuffle - reorder the collection in various ways
- swap - swap 2 elements of the collection
- indexOfSublist - find position of a sublist
- frequency - return frequency (count) of a given element in the collection

Notes:
- remember about Arrays.asList() method (convert an array to a List)
- check the constructors of common Set/List implementations - besides the default one (for an
empty collection), they usually have copy constructors (start with elements of existing collection)
Maps
Maps – Map interface

interface Map<K,V>

• A Map is a dictionary, storing key-value pairs


○ key-based access (not index-based)
○ keys must be unique
○ key and values can be of different types (K,V)

• Useful when you have to search, update or delete (more complex) elements
based on a (simpler) unique key
Maps – Map.Entry interface

interface Map<K,V> {
interface Entry<K,V> {…}

}

• Entry is a sub-interface of Map (may access its type by Map.Entry name)


• Used in some methods of Map
• Represents a key-value pair (encapsulating both parts in a single object)
○ contains methods to access the two parts (getKey, getValue, setValue)
Map – Methods (1)

- V get(Object key) - return the value for the specified key (if found)
- boolean containsKey(Object key) - returns true if map contains given key
- boolean containsValue(Object value) - returns true if map contains (at least) a key
with given value

- Set<Map.Entry<K, V>> entrySet() - returns a Set view with all entries


- Set<K> keySet() - returns a Set view with only the keys
- Collection<V> values() - returns a Collection view with only the values

- int size() - return number of entries


- boolean isEmpty() - return true if map is empty
Map – Methods (2)

- V put(K key, V value) - add a map entry (or update existing one)
- void putAll(Map<? extends K, ? extends V> map) - insert all entries of specified
map into this map
- V remove(K key) - remove an entry from map (and returns it)
- void clear() - removes all map entries

- V getOrDefault(Object key, V default) - returns the value of specified key, or


default if missing
- V putIfAbsent(K key, V value) - insert an entry to map only if key is not present
- V replace(K,V) - replace the value for some existing key
Maps – HashMap

• HashMap - implements the Map interface by using a hashtable

• Characteristics:
○ holds key-value pairs (unique keys)
○ maintains no order
○ based on hashing (needs correct hashCode()/equals() for keys)
○ nulls: allows one null key, multiple null values

https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html
Maps – Example

Map<Integer, String> m = new HashMap<>();


m.put(100, "Alex");
m.put(101, "Bogdan");
m.put(102, "Cristi");
m.put(102, "Catalin"); //overwrites value for existing key

m.remove(101);
for (Map.Entry e : m.entrySet()) {
System.out.println(e.getKey() + ": " + e.getValue());
}
//prints entries in random order
//-> 102: Catalin
//-> 100: Alex
Maps – LinkedHashMap

● LinkedHashMap - Hash table and linked list implementation of


the Map interface, with predictable iteration order

● Characteristics:
○ holds key-value pairs (unique keys)
○ maintains insertion order (unlike HashMap)
○ based on hashing (needs correct hashCode()/equals() for keys)
○ slightly slower than HashMap
○ nulls: allows one null key, multiple null values

https://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashMap.html
Maps – Example

Map<Integer, String> m = new LinkedHashMap<>();


m.put(100, "Alex");
m.put(101, "Bogdan");
m.put(102, "Cristi");
m.put(102, "Catalin"); //overwrites value for existing key

m.remove(101);
for (Map.Entry e : m.entrySet()) {
System.out.println(e.getKey() + ": " + e.getValue());
}
//prints entries in insertion order
//-> 100: Alex
//-> 102: Catalin
Maps – TreeMap

• TreeMap - implements the Map interface by using a tree, storing


in an efficient way the entries in sorted order (by key)

• Characteristics:
○ holds key-value pairs (unique keys)
○ maintains entries in sorted order (unlike HashMap)
■ key comparison: see Comparable or Comparator later
○ slower than HashMap, LinkedHashMap (due to sorting)
○ nulls: null keys not allowed, allows null values

https://docs.oracle.com/javase/8/docs/api/java/util/TreeMap.html
TreeMap– Example

Map<Integer, String> m = new TreeMap<>();


m.put(102, "Cristi");
m.put(100, "Alex");
m.put(101, "Bogdan");

for (Map.Entry e : m.entrySet()) {


System.out.println(e.getKey() + ": " + e.getValue());
}
//prints entries sorted alphabetically by key
//-> 100: Alex
//-> 101: Bogdan
//-> 102: Cristi
Map – common implementations
Maps – HashTable

● HashTable - implements Map using a hashtable

● Characteristics:
○ holds key-value pairs (unique keys)
○ maintains no order
○ based on hashing (needs correct hashCode()/equals() for keys)
○ nulls: doesn’t allow null keys or values
○ synchronized (safe for multi-threaded access)

● Considered deprecated - to be avoided! (old, slow, confusing api;


use ConcurrentHashMap instead, when needed)
https://docs.oracle.com/javase/8/docs/api/java/util/Hashtable.html
Enums
Enums - declaration

● enum - a special Java type used to define small collections of constants


○ is a special kind of Java class
○ all enums extend java.lang.Enum (and this cannot be changed/overridden)
○ besides constants, they may optionally contain fields, methods etc.

- Declaration: using ‘enum’ instead of class/interface, and giving the list of possible values:
enum Level {
HIGH, MEDIUM, LOW
}

- Referring the values:


Level level = Level.HIGH;
Enums - usage

- Comparing values - ok with “==”, no need for .equals() :

Level level = … ; //assign some Level constant to it

if (level == Level.HIGH) {/*do something*/}


else if (level == Level.MEDIUM) {/*do something else*/}
else if (level == Level.LOW) {/*else do something else*/}

- Iterating over all defined values:

for (Level level : Level.values()) {


System.out.println(level);
}
Enums - example

public enum Level { …


Level level = Level.HIGH;
HIGH(3), //calls constructor with value 3 System.out.println(level.getLevelCode());
MEDIUM(2), //calls constructor with value 2
LOW(1) //calls constructor with value 1 //prints out the value 3
; //optional, needed if fields/methods follow //which is the value of levelCode field
//for the enum constant HIGH
private final int levelCode;

Level(int levelCode) {
this.levelCode = levelCode;
}

public int getLevelCode() {


return this.levelCode;
}
}
EnumMap

class EnumMap < K extends Enum<K>, V > extends AbstractMap<K, V>

- EnumMap - a specialized Map implementation


- for keys of type enum (only)
- with better performance than HashMap

- commonly used constructor:


- EnumMap(Class<K> keyType) - creates an empty enum map
with the specified key type (K needs to extend Enum)
EnumMap – Example

import java.util.*;

class EnumMapExample {

//create an enum
enum Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }

public static void main(String[] args) {


//create and populate enum map
EnumMap<Days, String> map = new EnumMap<>(Days.class); //enum is just a kind of class
map.put(Days.Thursday, "4");
map.put(Days.Friday, "5");
map.put(Days.Saturday, "6");
map.put(Days.Sunday, "7");
map.put(Days.Monday, "1");
map.put(Days.Tuesday, "2");
map.put(Days.Wednesday, "3");

//print the entries of enum map, in the order of the constants in enum definition
for (Map.Entry m : map.entrySet()) { System.out.println(m.getKey() + " " + m.getValue()); }
}
}
Object.equals()
Object.equals()

Testing the equality of two values/objects:


- with ‘==’ operator -> works well only for primitives!

- with equals() - a method defined in base class Object, so inherited by all objects:

public class Object {


public boolean equals(Object obj) {
return this == obj; //default impl: same as “==” operator
}
}

You may override it in your classes, to make it test the logical equality of your objects!

Tip: IntelliJ IDEA can generate it: right click / Generate… / equals() and hashCode()
Object.equals() - Example

class Point {
private double x, y;
Point(int x, int y) { this.x = x; this.y = y; }

@Override
public boolean equals(Object obj) { //note the type: Object, not Point!
if (!(obj instanceof Point)) { return false; } //need to be of same type
Point that = (Point) obj; //may cast now from Object to specific type
return this.x == that.x && this.y == that.y; //check to have fields with same values
}
}

//using it to compare some Point instances:


Point origin = new Point(0, 0);
Point p1 = getSomePoint(…);
if (origin.equals(p1)) {…}
Object.equals() - Gotchas

Usage rule:
- == should be used for primitives (+ enums)
- equals() should always be used for reference types
- including String
- including wrapper types (Long, Integer, Double…)

Note: be careful when calling .equals(), as the object might be null !


- may use instead the static helper function Objects.equals() that also checks for null
String str = … ;
if (str == "some string") {} //Incorrect (not a primitive)
if (str.equals("some string")) {} //Correct, unsafe with null
if ("some string".equals(str)) {} //Correct, null safety, but harder to read
if (Objects.equals(str, "some string")) {} //Correct and safe
Object.equals() - Contract

equals() contract - the overridden equals() must be:


- reflexive: x.equals(x) == true for any x
- symmetric: x.equals(y) == y.equals(x) for any x,y
- transitive: x.equals(y) and y.equals(z) => x.equals(z)
- consistent: the value of equals() should change only if a property checked in
equals() changes (no randomness allowed)

Notes:
- pay attention to keeping equals() up to date (on changes of class structure/fields)
- pay attention to inheritance scenarios - if a base class overrides equals, derived
classes most probably need to override it too! (parent impl is not good enough)
Object.hashCode()
Object.hashCode()

- hashCode() - method inherited from Object by all reference types; may be overridden...
- return value:
- should be an integer value representing the current instance of the class
- the returned value should be consistent with definition of equality for that class

hashCode() contract - is closely related to equals() method:


- internal consistency: hashCode value may only change if a field used in equals() changes
- equals consistency: objects which are equal to each other must have the same hashCode
- collisions allowed: unequal objects may have the same hashCode
- for performance reasons, its desirable to have a hashCode with minimal collisions (and also fast)

Rule: equals() and hashCode() should always be overridden together!


Object.hashCode()

Why is this important?


- hash-based collections (HashMap, HashSet..) require a correct hashCode !
- if hashCode() breaks the contract, these collections may malfunction!

Implementation details:
- HashSet / HashMap(for keys): elements organized in a series of “buckets”, elements of a bucket having the
same hashCode value (may be multiple ones if collisions occurred)
- when putting a new object in a HashSet/HashMap:
- its hashCode value is computed, and based on it the element is added to the right bucket
- when getting an element from the collection, or checking existence:
- the hashCode of the given object is computed, and then based on this value the right bucket is
checked (and after this equals() will also be called)
- if hashCode is wrong/missing/breaks contract: may make collection actions wrong/unpredictable!
(failing to correctly add new elements, like allowing duplicates, or failing to get existing elements…)
Object.hashCode() - Example

class Cat { Cat c1 = new Cat("Pixel");


private String name; Cat c2 = new Cat("Pixel");
Cat(String name) { this.name = name; } Cat c3 = new Cat("Paka");

@Override System.out.println(c1.equals(c2)); //true


public String toString() { System.out.println(
return "Cat{name="+name+'}'; c1.hashCode() == c2.hashCode()); //false!!
}
Set<Cat> cats = new HashSet<>();
cats.add(c1); cats.add(c2); cats.add(c3);
@Override
public boolean equals(Object o) { //=> 3 cats in Set now, 2 of them equal!!
if (this == o) return true; // (but with different hashcode)
if (o==null||getClass()!=o.getClass()) System.out.println("cats:"+cats.size()); //3!!
return false;
return Objects.equals(name,((Cat) o).name); System.out.println(cats);
//=> [Cat{name=Pixel}, Cat{name=Paka}, Cat{name=Pixel}]
}
//but NO hashCode() override!! System.out.println("contains Pixel?: " +
} cats.contains(new Cat("Pixel"))); //false!
Questions?
Extra reading:
- http://tutorials.jenkov.com/java-collections/index.html
- https://www.geeksforgeeks.org/collections-in-java-2/
- https://en.wikipedia.org/wiki/Java_collections_framework
- https://www.baeldung.com/java-equals-hashcode-contracts

You might also like