0% found this document useful (0 votes)
14 views39 pages

Generics and Collections

Office docx

Uploaded by

Cherif Aidou
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)
14 views39 pages

Generics and Collections

Office docx

Uploaded by

Cherif Aidou
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/ 39

Generics and Collections

Note the redundancy


class Print{ class Print{
String o; Float o;
Print(String o){ Print(Float o){
this.o = o; this.o = o;
System.out.println(o); System.out.println(o);

} }

} }
Redundancy!
class Print{ class Print{
String o; Float o;
Print(String o){ Print(Float o){
this.o = o; this.o = o;
System.out.println(o); System.out.println(o);

} }

} }
Solution: Use Generics!
class Print<T>{
T o;
Print(T o){
this.o = o;
System.out.println(o);
}
}
Why Use Generics?

Generics enable types (classes and
interfaces) to be parameters when defining
classes, interfaces and methods.
– type parameters provide a way to re-use the
same code with different types of inputs.
Benefits of code that uses generics
● Stronger type checks at compile time.
● Elimination of casts.
– without generics requires casting:
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
– with generics, the code does not require casting:
List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0); // no cast

Enabling programmers to implement generic algorithms.
Generic Types

A generic type is a generic class or interface that is parameterized
over types.

a non-generic Box class that operates on objects of any type:
public class Box {
private Object object;
public void set(Object object) {
this.object = object;
}
public Object get() { return object; }
}
Generic Types

Since its methods accept or return an Object,
– you are free to pass in whatever you want,
– provided that it is not one of the primitive types.

There is no way to verify, at compile time, how the
class is used.
– One part of the code may place an Integer in the box and
expect to get Integers out of it,
– while another part of the code may mistakenly pass in a
String, resulting in a runtime error.
Generic Types

A generic class is defined with the following format:
class name<T1, T2, ... , Tn> {
/* ... */
}
– The type parameter section, delimited by angle brackets <>
– It specifies the type parameters (also called type variables)
T1, T2, ..., and Tn.
Generic Types

A Generic Version of the Box Class
public class Box<T> {
// T stands for "Type"
private T t;

public void set(T t) { this.t = t; }


public T get() { return t; }
}

All occurrences of Object are replaced by T.

A type variable can be any non-primitive type you specify:
– any class type, any interface type, any array type, or even another type variable.

This same technique can be applied to create generic interfaces.
Naming Conventions

By convention, type parameter names are single, uppercase letters.
– Without this convention, it would be difficult to tell the difference between a type variable
and an ordinary class or interface name.

The most commonly used type parameter names are:
– E - Element (used extensively by the Java Collections Framework)
– K - Key
– N - Number
– T - Type
– V - Value
– S,U,V etc. - 2nd, 3rd, 4th types
Invoking and Instantiating Generics

To reference the generic Box class from within your code,
you must perform a generic type invocation, which
replaces T with some concrete value, such as Integer:
Box<Integer> integerBox;
It declares (no instance) a reference to a "Box of Integer"

● To instantiate this class, use the new keyword, but place


<Integer> between the class name and the parenthesis:
Box<Integer> integerBox = new Box<Integer>();
Note: Terminology

Many developers use the terms "type parameter"
and "type argument" interchangeably, but these
terms are not the same.
– When coding, one provides type arguments in order to
create a parameterized type.
– Therefore, the T in Foo<T> is a type parameter and
the String in Foo<String> f is a type argument.
The Diamond <>

In Java SE 7 and later, you can replace the type
arguments required to invoke the constructor of a
generic class with an empty set of type arguments
(<>) as long as the compiler can determine, or infer,
the type arguments from the context.
Box<Integer> integerBox = new Box<>();
Multiple Type Parameters
public interface Pair<K, V> {
public K getKey();
public V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
private K key;
private V value;
public OrderedPair(K key, V value) {
this.key = key; this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Multiple Type Parameters
Pair<String, Integer> p1 =
new OrderedPair<String, Integer>("Even", 8);

Pair<String, String> p2 =
new OrderedPair<String, String>("hello", "world");

● new OrderedPair<String, Integer>


– instantiates K as a String and V as an Integer
– the parameter types of OrderedPair's constructor are String and Integer
– Due to autoboxing, it is valid to pass a String and an int to the class.
Shortening

The statements can be shortened using diamond notation:

OrderedPair<String, Integer> p1 =
new OrderedPair<>("Even", 8);
OrderedPair<String, String> p2 =
new OrderedPair<>("hello", "world");

– Java compiler can infer the K and V types from the declaration
OrderedPair<String, Integer>
Shortening

You can also substitute a type parameter with a parameterized
type.
OrderedPair<String, Box<Integer>> p = new
OrderedPair<>("primes", new Box<Integer>(...));
Raw Types

A raw type is the name of a generic class or interface without any type
arguments.
public class Box<T> {
public void set(T t) { /* ... */ }
// ...
}

To create a parameterized type of Box<T>, you supply an actual type
argument for the formal type parameter T:
Box<Integer> intBox = new Box<>();

If the actual type argument is omitted, you create a raw type of Box<T>:
Box rawBox = new Box();
Raw Types

Therefore, Box is the raw type of the generic type Box<T>.
– However, a non-generic class or interface type is not a raw type.

Raw types show up in legacy code because lots of API classes
(such as the Collections classes) were not generic prior to JDK
5.0.
– When using raw types, you essentially get pre-generics behavior — a
Box gives you Objects.
– For backward compatibility, assigning a parameterized type to its raw
type is allowed
Raw Types
Box<String> stringBox = new Box<>();
Box rawBox = stringBox; // OK

But if you assign a raw type to a parameterized
type, you get a warning:
Raw Types
Box rawBox = new Box(); // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox;//warning:unchecked conversion


You also get a warning if you use a raw type to invoke generic methods defined in
the corresponding generic type:
Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8); // warning: unchecked invocation to set(T)


The warning shows that raw types bypass generic type checks, deferring the
catch of unsafe code to runtime. Therefore, you should avoid using raw types.
Generic Methods

are methods that introduce their own type parameters.
– the type parameter's scope is limited to the method where it is
declared.
– Static and non-static generic methods are allowed, as well as generic
class constructors.

The syntax includes a list of type parameters, inside angle
brackets, which appears before the method's return type.
public static <K, V> boolean compare(
Pair<K, V> p1,
Pair<K, V> p2)
Generic Methods
● The Util class includes a generic method, compare, which compares two Pair
objects:

public class Util {


public static <K,V> boolean compare(Pair<K,V> p1, Pair<K,V> p2){
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
}
Generic Methods
public class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public void setKey(K key) { this.key = key; }
public void setValue(V value) { this.value = value; }
public K getKey() { return key; }
public V getValue() { return value; }
}
Generic Methods

The complete syntax for invoking this method would be:

Pair<Integer, String> p1 = new Pair<>(1, "apple");


Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);
Generic Methods

The type has been explicitly provided. Generally, this can be left
out and the compiler will infer the type that is needed:

Pair<Integer, String> p1 = new Pair<>(1, "apple");


Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);
Collections
Common Collection APIs

A collection is a group of objects contained in a single object.
– The Java Collections Framework is a set of classes in java.util

There are four main interfaces in the Java Collections
Framework.
– List: An ordered list of elements that allows duplicate entries.
– Set: A set is a collection that does not allow duplicate entries.
– Queue: A collection that orders its elements in a specific order.

A Deque is a subinterface of Queue that allows access at both ends.
– Map: A map is a collection that maps keys to values, with no duplicate
keys allowed. The elements in a map are key/value pairs
The interfaces are
shown in rectangles,
with the classes in
rounded boxes.
Diamond Operator

You can specify the type that will go inside the collection:
List<Integer> list = new ArrayList<Integer>();

You might even have generics that contain other generics:
Map<Long,List<Float>> map = new
HashMap<Long,List<Float>>();

the diamond operator (<>) is a shorthand notation that allows you
to omit the generic type from the right side of a statement when
the type can be inferred.
List<Integer> list = new ArrayList<>();
Map<Long,List<Integer>> mapOfLists = new HashMap<>();

The diamond operator cannot be used as the type in a variable
declaration. It can be used only on the right side of an assignment
operation.

For example, neither of the following compiles:
List<> list = new ArrayList<Integer>();
// DOES NOT COMPILE

class InvalidUse {
void use(List<> data) {} // DOES NOT COMPILE
}
Adding Data
● The add() method inserts a new element into the Collection and returns whether it
was successful.
public boolean add(E element)
3: Collection<String> list = new ArrayList<>();
4: System.out.println(list.add("Sparrow")); // true
5: System.out.println(list.add("Sparrow")); // true
6:
7: Collection<String> set = new HashSet<>();
8: System.out.println(set.add("Sparrow")); // true
9: System.out.println(set.add("Sparrow")); // false
//A Set does not allow duplicates.
Removing Data
● The remove() method removes a single matching value in the Collection and
returns whether it was successful.
public boolean remove(Object object)
//return value tells us whether a match was removed

3: Collection<String> birds = new ArrayList<>();


4: birds.add("hawk"); // [hawk]
5: birds.add("hawk"); // [hawk, hawk]
6: System.out.println(birds.remove("cardinal")); // false
7: System.out.println(birds.remove("hawk")); // true
8: System.out.println(birds); // [hawk]
Counting Elements
● The isEmpty() and size() methods look at how many elements are in the
Collection.
public boolean isEmpty()
public int size()
Collection<String> birds = new ArrayList<>();
System.out.println(birds.isEmpty()); // true
System.out.println(birds.size()); // 0
birds.add("hawk"); // [hawk]
birds.add("hawk"); // [hawk, hawk]
System.out.println(birds.isEmpty()); // false
System.out.println(birds.size()); // 2
Clearing the Collection
● The clear() method provides an easy way to discard all elements of the Collection.
public void clear()
Collection<String> birds = new ArrayList<>();
birds.add("hawk"); // [hawk]
birds.add("hawk"); // [hawk, hawk]
System.out.println(birds.isEmpty()); // false
System.out.println(birds.size()); // 2
birds.clear(); // []
System.out.println(birds.isEmpty()); // true
System.out.println(birds.size()); // 0
Check Contents
● The contains() method checks whether a certain value is in the Collection.
public boolean contains(Object object)

Collection<String> birds = new ArrayList<>();


birds.add("hawk"); // [hawk]
System.out.println(birds.contains("hawk")); // true
System.out.println(birds.contains("robin")); // false

● The contains() method calls equals() on elements of the ArrayList to


see whether there are any matches.
Iterating

An enhanced for loop.
for (String element: coll){
System.out.println(element);
}

● You may see another older approach used.


Iterator<String> iter = coll.iterator();
while(iter.hasNext()) {
String string = iter.next();
System.out.println(string);
}
Determining Equality
● There is a custom implementation of equals() so you can compare two Collections
to compare the type and contents. The implementation will vary. For example,
ArrayList checks order, while HashSet does not.
boolean equals(Object object)
23: var list1 = List.of(1, 2);
24: var list2 = List.of(2, 1);
25: var set1 = Set.of(1, 2);
26: var set2 = Set.of(2, 1);
27:
28: System.out.println(list1.equals(list2)); // false
29: System.out.println(set1.equals(set2)); // true
30: System.out.println(list1.equals(set1)); // false

You might also like