OOP L29 - L30: Generics and Dynamic Data Structures

You might also like

Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 21

OOP L29_L30

Generics and Dynamic Data Structures


Generics
• The term generics means parameterized types.
• Parameterized types are important because they enable you to create
classes, interfaces, and methods in which the type of data upon which
they operate is specified as a parameter.
• Using generics, it is possible to create a single class, for example, that
automatically works with different types of data.
• A class, interface, or method that operates on a parameterized type is
called generic, as in generic class or generic method.
Advantage of Java Generics
• Type-safety: We can hold only a single type of objects in generics. It doesn’t allow to store other
objects.
List list = new ArrayList();
list.add(10);
list.add("10");
With Generics, it is required to specify the type of object we need to store.
List<Integer> list = new ArrayList<Integer>();
list.add(10);
list.add("10"); // compile-time error
• Type casting is not required: There is no need to typecast the object.
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); //typecasting
After Generics, we don't need to typecast the object.
List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);
• Compile-Time Checking: It is checked at compile time so problem will not occur at runtime. The
good programming strategy says it is far better to handle the problem at compile time than runtime.
Generic Class
• A class that can refer to any type of object is known as a generic class.
Here, we are using the T type parameter to create the generic class of
specific type.
• Here is the syntax for declaring a generic class:
class class-name<type-param-list> { // ...
• Here is the syntax for declaring a reference to a generic class:
class-name<type-arg-list> var-name =
new class-name<type-arg-list>(cons-arg-list);
Example:
class Gen<T> { class GenDemo {
T ob; // declare an object of type public static void main(String args[]) {
T Gen<Integer> iOb;
Gen(T o) { // Notice the use of autoboxing
ob = o; // to encapsulate the value 88 within an Integer object.
iOb = new Gen<Integer>(88);
}
iOb.showType();
T getob() { int v = iOb.getob();
return ob; System.out.println("value: " + v);
} System.out.println();
Gen<String> strOb = new Gen<String>("Generics
void showType() {
Test");
System.out.println("Type of T is " strOb.showType();
+ String str = strOb.getob();
ob.getClass().getName()); System.out.println("value: " + str);
} }
} }
Note: A generic class can have more than one parameter.
Bounded Type
• When specifying a type parameter, you can create an upper bound that
declares the superclass from which all type arguments must be
derived.
• This is accomplished through the use of an extends clause when
specifying the type parameter, as shown here:
<T extends superclass>
• This specifies that T can only be replaced by superclass, or subclasses
of superclass. Thus, superclass defines an inclusive, upper limit.
Example:
class BoundsDemo {
class Stats<T extends Number> { public static void main(String args[]) {
T[] nums; // array of Number or subclass Integer inums[] = { 1, 2, 3, 4, 5 };
Stats<Integer> iob = new
Stats(T[] o) {
Stats<Integer>(inums);
nums = o; double v = iob.average();
} System.out.println("iob average is " + v);
double average() { Double dnums[] = { 1.1, 2.2, 3.3, 4.4,
5.5 };
double sum = 0.0; Stats<Double> dob = new
for(int i=0; i < nums.length; i++) Stats<Double>(dnums);
sum += nums[i].doubleValue(); double w = dob.average();
System.out.println("dob average is " +
return sum / nums.length; w);
} }}
} Output:
Average is 3.0
Average is 3.3
Using Wildcard Arguments
• Given the Stats class in the preceding example, assume that you want to add a method
called sameAvg( ) that determines if two Stats objects contain arrays that yield the
same average, no matter what type of numeric data each object holds.
• One way to implement sameAvg( ) is to pass it a Stats argument, and then compare the
average of that argument against the invoking object, returning true only if the
averages are the same.
Integer inums[] = { 1, 2, 3, 4, 5 };
Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
Stats<Integer> iob = new Stats<Integer>(inums);
Stats<Double> dob = new Stats<Double>(dnums);
if(iob.sameAvg(dob))
System.out.println("Averages are the same.");
else
System.out.println("Averages differ.");
• Trouble starts as soon as you try to declare a parameter of type Stats. Because
Stats is a parameterized type, what do you specify for Stats’ type parameter
when you declare a parameter of that type?
• At first, you might think of a solution like this, in which T is used as the type
parameter:
boolean sameAvg(Stats<T> ob) {
if(average() == ob.average())
return true;
return false;
}
• The trouble with this attempt is that it will work only with other Stats objects
whose type is the same as the invoking object. For example, if the invoking
object is of type Stats<Integer>, then the parameter ob must also be of type
Stats<Integer>.
• To create a generic sameAvg( ) method, you must use another feature
of Java generics: the wildcard argument.
• The wildcard argument is specified by the ?, and it represents an
unknown type.
• Using a wildcard, here is one way to write the sameAvg( ) method:
boolean sameAvg(Stats<?> ob) {
if(average() == ob.average())
return true;
return false;
}
• Here, Stats matches any Stats object, allowing any two Stats objects to
have their averages compared.
Bounded Wildcards
• We can have bounded wildcards just like bounded types.
• In general, to establish an upper bound for a wildcard, use the following type of
wildcard expression:
<? extends superclass>
• where superclass is the name of the class that serves as the upper bound.
Remember, this is an inclusive clause because the class forming the upper bound
(that is, specified by superclass) is also within bounds.
• You can also specify a lower bound for a wildcard by adding a super clause to a
wildcard declaration. Here is its general form:
<? super subclass>
• In this case, only classes that are superclasses of subclass are acceptable arguments.
Generic Method
• Methods inside a generic class can make use of a class’s type parameter and are,
therefore, automatically generic relative to the type parameter.
• It is possible to declare a generic method that uses one or more type parameters of
its own.
• It is possible to create a generic method that is enclosed within a non-generic class.
• Static and non-static generic methods are allowed, as well as generic class
constructors.
• The syntax for a generic method includes a list of type parameters, inside angle
brackets, which appears before the method's return type.
<type-param-list> ret-type meth-name(param-list) { // ...
• It is also possible for constructors to be generic, even if their class is not.
Example: To determine if an object is a member // Use isIn() on Strings.
of an array String strs[] = { "one", "two", "three",
class GenMethDemo { "four", "five" };
// Determine if an object is in an array. if(isIn("two", strs))
System.out.println("two is in strs");
static <T, V extends T> boolean isIn(T x, V[] y) {
if(!isIn("seven", strs))
for(int i=0; i < y.length; i++) System.out.println("seven is not in strs");
if(x.equals(y[i])) return true; // Oops! Won't compile! Types must be
return false; compatible.
// if(isIn("two", nums))
} // System.out.println("two is in strs");
public static void main(String args[]) { }
Integer nums[] = { 1, 2, 3, 4, 5 }; }
if(isIn(2, nums)) Output:
System.out.println("2 is in nums"); 2 is in nums
if(!isIn(7, nums)) 7 is not in nums
System.out.println("7 is not in nums"); two is in strs
seven is not in strs
System.out.println();
Generic Interfaces
• Generic interfaces are specified just like generic classes.
• The generic interface offers two benefits.
• First, it can be implemented for different types of data.
• Second, it allows you to put constraints (that is, bounds) on the types of data
for which the interface can be implemented.
• The generalized syntax for a generic interface
interface interface-name <type-param-list>{ // ..
• When a generic interface is implemented, you must specify the type arguments, as
shown here:
class class-name<type-param-list> implements interface-name<type-arg-list> {
Example: To return the minimum and maximum value of
some set of objects
public T max() {
interface MinMax<T extends Comparable<T>> {
T v = vals[0];
T min(); for(int i=1; i < vals.length; i++)
if(vals[i].compareTo(v) > 0) v = vals[i];
T max();
return v;}
} }
class GenIFDemo {
class MyClass<T extends Comparable<T>> implements public static void main(String args[]) {
MinMax<T> { Integer inums[] = {3, 6, 2, 8, 6 };
T[] vals; Character chs[] = {'b', 'r', 'p', 'w' };
MyClass<Integer> iob = new
MyClass(T[] o) { vals = o; } MyClass<Integer>(inums);
public T min() { MyClass<Character> cob = new
MyClass<Character>(chs);
T v = vals[0]; System.out.println("Max value in inums: " +
for(int i=1; i < vals.length; i++) iob.max());
System.out.println("Min value in inums: " +
if(vals[i].compareTo(v) < 0) v = vals[i]; iob.min());
return v; System.out.println("Max value in chs: " + cob.max());
System.out.println("Min value in chs: " + cob.min());
} }
}
Dynamic Data Structures in Java
• Dynamic data structures are those in which the size of the structure can be changed
at runtime.
• Since these data structures can grow or shrink as needed, they are often used for
more complex and larger data sets.
Examples of Dynamic Data Structures:
• Singly Linked List
• Doubly Linked List
• Vector
• Stack
• Queue
• Tree
Vector
• The Vector class implements a growable array of
objects.
• Vectors basically fall in legacy classes but now it
is fully compatible with collections.
• Vector implements a dynamic array that means it
can grow or shrink as required.
• Like an array, it contains components that can be
accessed using an integer index.
Example:
import java.util.*;
public class VectorExample {
public static void main(String args[]) {
Vector<String> vec = new Vector<String>();
vec.add("Tiger");
vec.add("Lion");
vec.add("Dog");
vec.add("Elephant");
vec.addElement("Rat");
vec.addElement("Cat");
vec.addElement("Deer");

System.out.println("Elements are: "+vec);


}
}
Difference between ArrayList and Vector
ArrayList Vector
1) ArrayList is not synchronized. Vector is synchronized.
2) ArrayList increments 50% of current Vector increments 100% means doubles
array size if the number of elements the array size if the total number of
exceeds from its capacity. elements exceeds than its capacity.
3) ArrayList is not a legacy class. It is Vector is a legacy class.
introduced in JDK 1.2.
4) ArrayList is fast because it is non- Vector is slow because it is synchronized,
synchronized. i.e., in a multithreading environment, it
holds the other threads in runnable or non-
runnable state until current thread releases
the lock of the object.
Stack
• Java Collection framework provides a Stack class which models and
implements a Stack data structure.
• The class is based on the basic principle of last-in-first-out.
• In addition to the basic push and pop operations, the class provides
three more functions of empty, search and peek.
• The class can also be said to extend Vector class.
Example:
import java.util.Stack;
public class StackEmptyMethodExample
{
public static void main(String[] args)
{
Stack<Integer> stk= new Stack<>(); Output:
Is the stack empty? true
boolean result = stk.empty();
Elements in Stack: [78, 113, 90,
System.out.println("Is the stack empty? " + result); 120]
stk.push(78); Is the stack empty? false
stk.push(113);
stk.push(90);
stk.push(120);
System.out.println("Elements in Stack: " + stk); You can try implementing the other
result = stk.empty(); dynamic data structures by your own.
System.out.println("Is the stack empty? " + result);
}
}

You might also like