OOP L29_L30
Generics and Dynamic Data Structures
Generics
• It eliminates the need to create multiple versions of methods or classes
for various data types so basically we can use one version of a method
or a class for all reference data types.
• 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();
[Link](10);
[Link]("10");
With Generics, it is required to specify the type of object we need to store.
List<Integer> list = new ArrayList<Integer>();
[Link](10);
[Link]("10"); // compile-time error
• Type casting is not required: There is no need to typecast the object.
List list = new ArrayList();
[Link]("hello");
String s = (String) [Link](0); //typecasting
After Generics, we don't need to typecast the object.
List<String> list = new ArrayList<String>();
[Link]("hello");
String s = [Link](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 T public static void main(String args[]) {
Gen(T o) { Gen<Integer> iOb;
ob = o; // Notice the use of autoboxing
// to encapsulate the value 88 within an Integer object.
}
iOb = new Gen<Integer>(88);
T getob() { [Link]();
return ob; int v = [Link]();
} [Link]("value: " + v);
[Link]();
void showType() {
Gen<String> strOb = new Gen<String>("Generics Test");
[Link]("Type of T is " + [Link]();
[Link]().getName()); String str = [Link]();
} [Link]("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(T[] o) { Stats<Integer> iob = new
nums = o; Stats<Integer>(inums);
} double v = [Link]();
[Link]("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 < [Link]; i++) Stats<Double>(dnums);
sum += nums[i].doubleValue(); double w = [Link]();
return sum / [Link]; [Link]("dob average is " + 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([Link](dob))
[Link]("Averages are the same.");
else
[Link]("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() == [Link]())
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() == [Link]())
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 of // Use isIn() on Strings.
an array
class GenMethDemo { String strs[] = { "one", "two", "three",
"four", "five" };
// Determine if an object is in an array.
if(isIn("two", strs))
static <T, V extends T> boolean isIn(T x, V[] y) { [Link]("two is in strs");
for(int i=0; i < [Link]; i++) if(!isIn("seven", strs))
if([Link](y[i])) return true; [Link]("seven is not in strs");
return false; // Oops! Won't compile! Types must be
} compatible.
public static void main(String args[]) { // if(isIn("two", nums))
// [Link]("two is in strs");
Integer nums[] = { 1, 2, 3, 4, 5 };
}
if(isIn(2, nums)) }
[Link]("2 is in nums");
if(!isIn(7, nums)) Output:
[Link]("7 is not in nums"); 2 is in nums
7 is not in nums
[Link]();
two is in strs
seven is not in strs
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 < [Link]; 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]; [Link]("Max value in inums: " +
for(int i=1; i < [Link]; i++) [Link]());
[Link]("Min value in inums: " +
if(vals[i].compareTo(v) < 0) v = vals[i]; [Link]());
return v; [Link]("Max value in chs: " + [Link]());
[Link]("Min value in chs: " + [Link]());
} }
}
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 [Link].*;
public class VectorExample {
public static void main(String args[]) {
Vector<String> vec = new Vector<String>();
[Link]("Tiger");
[Link]("Lion");
[Link]("Dog");
[Link]("Elephant");
[Link]("Rat");
[Link]("Cat");
[Link]("Deer");
[Link]("Elements are: "+vec);
}
}
Difference between ArrayList and Vector
ArrayList Vector
1) ArrayList is not synchronized. Vector is synchronized.
2) ArrayList increments 50% of Vector increments 100% means
current array size if the number of doubles the array size if the total
elements exceeds from its capacity. number of elements exceeds than
its capacity.
3) ArrayList is not a legacy class. It Vector is a legacy class.
is introduced in JDK 1.2.
4) ArrayList is fast because it is Vector is slow because it is
non-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 [Link];
public class StackEmptyMethodExample
{
public static void main(String[] args)
{
Stack<Integer> stk= new Stack<>(); Output:
boolean result = [Link](); Is the stack empty? true
[Link]("Is the stack empty? " + result); Elements in Stack: [78, 113, 90,
[Link](78); 120]
[Link](113); Is the stack empty? false
[Link](90);
[Link](120);
[Link]("Elements in Stack: " + stk);
result = [Link](); You can try implementing the other
[Link]("Is the stack empty? " + result); dynamic data structures by your own.
}
}