java.util
Package and the Collection FrameworkThe java.util package has lots of classes and interfaces. These are mainly related to the collection framework. Apart from the collection framework, it has many utility classes and also the date and time related classes. In Java, time is normally known and used as a long value in milliseconds from a standard base time known as “The Epoch”. Epoch is the time 1st January, 1970 0:0:0.0 GMT, which would be the same as 1st January, 1970 5:30:0.0 IST. There are many classes which encapsulate the millisecond value and provide methods to convert and extract the values related to the day, month, year, hour, minutes, seconds, etc. There are two main classes which use this, namely java.util.Date and java.util.Calendar. These are discussed in Section 13.1. The collection framework provides the various classes and interfaces related to most of the data structures in Java. The collection framework has been discussed in Section 13.3.
Date
, TimeZone
, Calendar
AND THE GregorianCalendar
CLASSESThe Date
class in java.util package represents a specific instance in time, with millisecond precision. Most of the methods and constructors have been deprecated in view of the Calendar
class, which replaces most of the functionality of the Date
class. The Calendar
class is more flexible and is more suited for internationalization. Let us look at some of the constructors and methods from the Date
class.
The Date
class has a constructor with no arguments. This constructor creates an instance of Date
which represents the instance of time when the Date
object was allocated. The instance of time is the one which is available from the platform. The current time from the platform is also available by using the method currentTimeMillis
(). Another constructor of the Date
class allows us to create an instance of Date
by specifying time in milliseconds since the epoch. The common constructor and methods from the Date
class are given in Listing 13.1.
Listing 13.1. Constructors and common methods from Date
class
1 // constructors 2 public Date() 3 public Date(long date) 4 // methods 5 public long getTime() 6 public void setTime(long time) 7 public int compareTo(Date anotherDate) 8 public boolean before(Date when) 9 public boolean after(Date when) 10 public String toString() 11 public boolean equals(Object obj)
The absolute millisecond value from epoch can be converted to a date in a specific calendar. The conversion depends on the time zone value. The Calendar
class is an abstract base class. An instance of Calendar
class maintains the time in millisecond value as well as the calendar field values like day, month, year, hour, minutes, seconds and era. The synchronization between the millisecond time value and the calendar fields is done whenever it is required because of some method invocation like get
(). When doing this conversion from milliseconds to the calendar field values, there are many fields which are affected by the value of the time zone. The Calendar
class also has a setting for the time zone offset value.
We have a class called TimeZone
in the java.util package, which can represent a time zone. The instance of TimeZone
maintains the offset value. The offset value of a time zone may be dependent on date because of the daylight savings applicable in those time zones. The time zone instances have an ID. Listing 13.2 gives some of the common methods. There is always a default time zone which is used by JVM. Initially it is the same as that of the native system. This time zone instance is available using the static
method getDefault
().
Listing 13.2. TimeZone
methods
1 public static TimeZone getDefault() 2 public static void setDefault(TimeZone zone) 3 public String getID() 4 public String getDisplayName() 5 public int getOffset(long date) 6 public int getRawOffset() 7 public void setRawOffset(int offsetMillis) 8 public static String[] getAvailableIDs() 9 public static String[] getAvailableIDs(int rawoffset) 10 public static TimeZone getTimeZone(String ID) 11 public static TimeZone getTimeZone(String id)
The Calendar
class has getTime
() and setTime
() methods which can be used to get and set the millisecond value in the calendar using a Date
instance.
The GregorianCalendar
is the concrete sub-class of Calendar
for interpreting the millisecond value from epoch according to the gregorian calendar.
From Java 5 onwards, we have a class called Formatter
which can be used for interpreting the long millisecond value as a date from a calendar assuming a time zone. The instance of TimeZone
class represents the time zone relative to UTC.
Date formatting can be done using java.text.SimpleDateFormat class.
(a) create an instance of SimpleDateFormat
using a formatting String
, e.g. "dd-MMM-yyyy"
(b) use format
() method to convert Date
to String
and (c) use parse
() method to convert String
to Date
.
e.g.
1 DateFormat dateFormat = new DateFormat("dd-MMM-yyyy"); 2 Date d = new Date(); 3 String s = dateFormat.format(d); 4 5 String s = "25-Dec-2005"; 6 Date d = dateFormat.parse(s);
Arrays
CLASSThe Arrays
class contains lots of utility methods related to operations on all types of arrays. These are all static
methods and no instance is required to be created in order to use these utility methods. It has methods called fill
(), equals
(), sort
(), binarySearch
(), toString
(), etc. All these methods are overloaded to support operations on any kind of array.
The fill
() method could be used to fill the entire or a segment of any array with the same value. In general the method is of the form
1 public static void fill(xxx[] array, xxx value} 2 public static void fill(xxx[] array, int fromIndex, int toIndex , xxx value}
where xxx could be any data type, e.g. if we have an integer array a
and want to fill all its elements with a value of -1, then we can do it as given below:
1 Arrays.fill(a, -1);
The equals
() method returns a boolean value, it takes two parameters of the same type of array. It makes a comparision for equality of each and every element in the two array, and also that the size of the two arrays should be the same.
The sort
() can be used to sort any type of array. The sort
() method for the reference type can also take a Comparator
as a parameter.
The binarySearch
() can be used to search the index position of the element with a particular value. The binary search can be done only on sorted arrays.
Collection
FRAMEWORKThe Collection
Framework was introduced in Java from 1.2. Prior to Java 1.2 there were independent classes for the various implementations of the Data Structures. The Collection
framework was introduced with the purpose of standardizing and having interoperability between various data structures. The framework relies on a standard set of interfaces which may be implemented in different ways by the actual data structure implementation classes. The interfaces also have methods to allow for interoperation between the various data structure implementation classes. Here the data structures are used to manage instances and not for managing primitive types. For primitive types we only have arrays. The primitives may be used in the methods of the collection framework from Java 5.0 onwards, since we now also have the boxing and unboxing conversion, which will convert any primitive value to its wrapper instance if it is passed as a parameter. Some of the important interfaces from the Collection
Framework are given in Figure 13.1.
Collection
InterfaceThe base interface is the Collection
interface which represents any kind of collection of Objects. The methods in this interface define the kind of operations which may be implemented by the various data structure implementation classes. The operations which can be performed on a collection in general can be categorized into basic operations, array operations and bulk operations. Also, as part of the collection framework specifications. It is advised that all non-abstract implementations of the Collection
interface would provide at least one constructor which takes another Collection
as a parameter and would then be initialized to contain all the elements from the given collection.
Figure 13.1 Collection
interface hierarchy
Basic operations: The basic operations for the base Collection
interface are given in Listing 13.3.
Listing 13.3. Basic operations on collection
1 public boolean add(Object o) 2 public boolean remove(Object o) 3 public int size() 4 public void clear() 5 public boolean contains(Object o) 6 public boolean isEmpty() 7 public Iterator iterator();
The add
() method is used to add a new Object
into the collection.
The remove
() method can remove the specified Object
from the collection, this method uses the equals
() method to determine the object to be removed. It removes only one instance from the collection for which the equals
() method returns true
. If there is no such Object
in the collection then this method would simply return false
.
The size
() method returns the number of elements currently in the collection.
The clear
() method removes all the elements from the collection, so after call to this method, size
() would be 0.
The contains
() method would check if the specified Object
exists in the collection. It would use the equals
() method with every element, and if it returns true
for any element, then this method would return true
.
The isEmpty
() method would return true
if the size is 0.
The iterator
() method returns an instance of the Iterator
interface (some class which implements the Iterator
interface). An instance of the Iterator
always has an underlying collection. These methods are used for iterating over all the elements of the underlying collection. The methods of the Iterator
interface are given in Listing 13.4.
Listing 13.4. Methods of Iterator
1 public boolean hasNext() 2 public Object next() 3 public void remove()
We can iterate over the elements of the underlying collection of an Iterator
by using the next
() method. The hasNext
() method returns true
, if there are more elements remaining for iteration, and the remove
() method would remove the instance from the collection which was last accessed using the next
() method. These methods allow us to have the following kind of code to iterate over the elements of a collection. Let us assume that we have a collection which only contains the objects of the Account
type.
1 … 2 Collection c = …; 3 … 4 Iterator iter = c.iterator(); 5 Account ac = null; 6 while (iter.hasNext()) { 7 … 8 ac = (Account)iter.next(); 9 … 10 if (…) { 11 iter.remove(); 12 } 13 … 14 }
Array operations: The array operations for the base Collection
interface are given in Listing 13.5.
Listing 13.5. Array operations on a collection
1 public Object[] toArray() 2 public Object[] toArray(Object[] o)
The first toArray
() method would always return an instance of the Object[], whose size is the same as the size of the collection. The array returned by the second method differs from the one returned by the first, here the array is not an instance of Object[], but instead it is of the type which matches the instance passed as parameter. If we pass an array of Account
as a parameter then the instance returned will also be an Account[], so we can cast it and assign to the Account[]. Also the array which is passed as the parameter is also updated with the elements from the collection.
Now there are three cases regarding the size of the array which is passed as the parameter and the size of the collection. The size of the array may be less than the size of the collection or the size of the array may be greater than the size of the collection, or the sizes of the array and collection are the same. In all cases the size of the array returned matches the size of the collection. In case the size of the array is less than the size of the collection, the method would update whatever elements can be accomodated from the collection in the array and allocate a new array whose size is the same as the size of the collection, which is updated and returned by the method. Example, if the size of the collection is 10 elements and the array passed is of size 5, then the array which is passed is updated to have any of the 5 elements from the collection and it also allocates a new array of size 10, updates it by filling all the elements from the collection and returns the newly allocated array. If the size of the array passed is of size 15, then the array which is passed is updated such that the first 10 elements are the elements in the collection. The next 5 values are set to null and it also allocates a new array of size 10, updates it by filling all the elements from the collection and returns the new allocated array. If the size of the array is 10 then it would be updated from the elements of the collection and no new array is allocated, it returns the same instance which was given as the parameter.
A common way of using the second toArray
method is as given in the listing below:
1 … 2 Collection c = … 3 … 4 Account[] ac = (Account[])c.toArray(new Account[c.size()]); 5 …
Here the Account[] ac would be the instance of the array created in the parameter using
1 new Account[c.size()]
Bulk operations: The bulk operations for the base Collection
interface are given in Listing 13.6. These methods allow us to have interoperability between various types of collections.
Listing 13.6. Bulk operations on Collection
1 public boolean addAll(Collection c) 2 public boolean removeAll(Collection c) 3 public boolean retainAll(Collection c) 4 public boolean containsAll(Collection c)
Let us say we have two references a
and b
which refer to two different Collection
instances, then a.addAll(b) would result in a = a union b. a.removeAll(b) would result in a = a - b,
a.retainAll(b) would result in a = a intersection b, whereas a.containsAll(b) would check if b is a subset of a. An implementation of Collection
is not very commonly used. It is the various subinter-faces of Collection
whose concrete implementation classes are used. There is one abstract base class called AbstractCollection
which implements the Collection
interface.
Set
and List
InterfacesWhat is a Set
? In set theory, Set
is a collection of unique elements; similarly here also the Set
interface is used as an interface for implementations of those collections which ensure uniqueness of elements. A Set
is a Collection
which does not have duplicates, i.e. there cannot be any two elements a
and b
in a Set
such that a.equals(b) would return true
. The Set
interface inherits all the methods from the Collection
interface. It does not have any additional methods.
The commonly used concrete implementation class for the Set
interface is the HashSet
class. There is also a base abstract class implementing the Set
interface. This is the AbstractSet
class. The HashSet
class extends from this abstract class.
A list is an ordered Collection
where elements are maintained by index positions.
The List
interface extends
the Collection
interface. The basic operations inherited from the base Collection
interface are given in Listing 13.3. The additional basic operations are given in Listing 13.7.
Listing 13.7. Additional basic methods for List
1 public void add(int index, Object o) 2 public Object remove(int index) 3 public Object get(int index) 4 public Object set(int index, Object o) 5 public int indexOf(Object o) 6 public int lastIndexOf(Object o) 7 public List subList(int start, int end) 8 public ListIterator listIterator() 9 public ListIterator listIterator(int index)
These methods involve the indexes. The add
() method with index value inserts the Object
at the given index position. So if a List
has 10 elements their index positions are from 0–9. Now if we add a new object at index position 5, then elements from 5–9 will be shifted to 6–10 and the new object will be at index position 5. Similarly, elements can be removed using the index position using the remove
() method with index. The remove
() method returns the instance which has been removed from the list. The get
() method can be used to obtain an object at the given index position. The set
() method replaces an instance at the given index position in the List
with the new instance, and returns the old instance which has been replaced.
The indexOf
() and the lastIndexOf
() methods search for an element and return the first found element’s index position in the List
. The indexOf
() method searches from left to right, whereas the lastIndexOf
() method searches from right to left. If the element is not found in the List
, then these methods return a -1. The subList
() method creates a new List
with elements between the specified start and end index positions within the List
. The elements included in the new List
are the elements from index positions start index upto but not including the element at the end index position in the current List
. The last two methods return an instance of ListIterator
, which allows us to iterate over all the elements in the List
.
In case of a List
the iteration can be done in both directions using the ListIterator
. A ListIterator
always has an underlying List
and has methods for navigating in both directions. The ListIterator
interface extends from the Iterator
interface. The methods in the ListIterator
are given in Listing 13.8.
Listing 13.8. Methods of ListIterator
1 public boolean hasNext() 2 public Object next() 3 public int nextIndex() 4 5 public boolean hasPrevious() 6 public Object previous() 7 public int previousIndex() 8 9 public void remove() 10 public void add(Object o) 11 public void set(Object o)
The first three methods are the methods in the forward direction. The ListIterator
maintains a current position within the underlying List
. This position is between two index positions, identified by the previous index and the next index. The nextIndex
() and the previousIndex
() methods return the previous and the next index values with respect to the current position. The next
() method retrieves the instance which is currently at the next index position in the list, and the current position increases by one, i.e. the values of the next index and previous index will increase by one. Similarly the previous
() method retrieves the instance which is at the previous index position, and decreases the current index by one. The hasNext
() and hasPrevious
() methods may be used before calling next
() and the previous
() methods to ensure that the next index is not outside the list range.
The last three methods remove
(), add
() and set
() will update the underlying List
. The remove
() method removes the last accessed element. The element may have been accessed either by using the next
() or the previous
() method. The set
() method replaces the last accessed element in the underlying List
and the add
() method inserts the new element at the current position, i.e. between the previous index and the next index in the underlying List
.
The bulk operations given in Listing 13.3 are inherited from the Collection
interface by the List
interface. The additional bulk operations are given in Listing 13.9.
Listing 13.9. Additional bulk operations in List
1 public void addAll(int index, Collection c)
The addAll
() method with index value inserts elements from any collection into a list starting at the specified index position.
The commonly used concrete implementation classes for the List
interface are the ArrayList
and the Vector
classes. Here the Vector
class is a legacy class. It is part of the Java API, right from Java 1.0.
These two classes have been derived (inherited) from the AbstractList
class. The AbstractList
class is an abstract class implementing the List
interface. This class has implementations of some of the methods from the interface, and if we want to create our own implementation of List
interface, then this class is a good starting point.
Vector
ClassThe Vector
class was modified from Java 1.2 to implement the List
interface. Also it was made to extend from the AbstractList
class rather than from the Object
class, which was the case before Java 1.2. There are a lot of other methods in the Vector
class which are the methods right from 1.0, which are doing the same activities as the methods of the List
interface. The constructors and additional methods (methods not already in the List
interface) for the Vector
class are given in Listing 13.10.
Listing 13.10. Vector
class constructors and methods
1 public Vector() 2 public Vector(int initialCapacity) 3 public Vector(int initialCapacity, int capacityIncrement) 4 public void addElement(Object obj) 5 public void insertElementAt(Object obj, int index) 6 public boolean removeElement(Object obj) 7 public void removeElementAt(int index) 8 public void removeAllElements() 9 public void setElementAt(Object obj, int index) 10 public void copyInto(Object[] anArray) 11 public Object elementAt(int index) 12 public Object firstElement() 13 public Object lastElement() 14 public int indexOf(Object obj, int start) 15 public int lastIndexOf(Object obj, int start) 16 public Enumeration elements() 17 public int capacity() 18 public void ensureCapacity(int minCapacity) 19 public void trimToSize() 20 public void setSize(int newSize)
Every Vector
has a capacity and a capacityIncrement
. The capacity of the Vector
will be greater than or equal to the size of the Vector
. The capacityIncrement
is the value by which the capacity is increased if the capacity is required to be increased. If the capacityIncrement
is set to 0, then the capacity is always doubled whenever it is required to be increased. The value of the capacityIncrement
can be specified in the constructor only. If the value of the capacityIncrement
is not specified then its default value is 0.
The addElement
() method is equivalent to the add
() method with one parameter of the List
interface. The insertElementAt
() method is equivalent to the add
() method with two parameters in the List
interface. Similarly the methods removeElement
() and removeElementAt
() are equivalent to the remove
() methods in the List
interface. The removeAll
() method is equivalent to the clear
() method in the List
interface. The copyInto
() method takes an Object[] as a parameter and will update it similar to the toArray
() method. Note that here the method does not return any array, it only updates the array which is passed as a parameter.
The methods elementAt
() and the setElementAt
() are equivalent to the get
() and set
() methods in the List
interface. The firstElement
() and lastElement
() are simply convenience methods to get the first and the last element from the Vector
. The methods indexOf
() and lastIndexOf
() are overloaded to allow us to specify the start index for the search.
The capacity
() returns the current capacity of the Vector
. The method ensureCapacity
() increases the capacity to at least the value specified if it is less than the specified value and the method trimToSize
() reduces the capacity of the Vector
to match the size. The setSize
() method can change the size of the Vector
. When the size is reduced then the last elements are lost and when the size is increased, then the elements at the increased indexes are nulls.
To iterate over all the elements of a Vector
, we have a method called elements
(). This method returns an instance of the Enumeration
interface. This interface is a legacy interface to iterate over various elements. This interface has only two methods as given in Listing 13.11. There are a lot of places in the API, where we come across the Enumeration
interface.
Listing 13.11. Methods of Enumeration
1 public boolean hasMoreElements() 2 public Object nextElement()
EXERCISE 13.1 Update the Account
class of Exercise 12.2 to using List
to maintain all the Transaction
instances instead of the array. Also update the printPassbook
() method accordingly.
The updated Account
class using List
instead of the array earlier is given in Listing 13.12.
Listing 13.12. Account
class using List
to maintain Transactions
1 package bank; 2 3 import java.util.Date; 4 import java.util.List; 5 import java.util.ArrayList; 6 7 public abstract class Account implements Comparable<Account> { 8 9 private int accountNumber; 10 private String name; 11 // protected double balance; 12 private double balance; 13 14 // private Transaction[] passbook = new Transaction[100]; 15 // private int nextIndexInPassbook = 0; 16 private List passbook = new ArrayList(); 17 18 public class Transaction { 19 private Date dateOfTransaction = new Date(); 20 private String naration; 21 private boolean credit; 22 private double amount; 23 24 public Transaction(String n, boolean c, double amt) throws NegativeAmountException { 25 if (amt < 0) { 26 throw new NegativeAmountException("Negative "+(c ? "credit" : "debit"), amt, Account.this); 27 } 28 this.naration = n; 29 this.credit = c; 30 this.amount = amt; 31 Account.this.balance += getNetAmount(); 32 // Account.this.passbook[nextIndexInPassbook++] = this; 33 passbook.add(this); 34 } 35 36 public Date getDateOfTransaction() { 37 return this.dateOfTransaction; 38 } 39 40 public String getNaration() { 41 return this.naration; 42 } 43 44 public boolean isCredit() { 45 return this.credit; 46 } 47 48 public double getAmount() { 49 return this.amount; 50 } 51 52 public double getNetAmount() { 53 return this.credit ? this.amount : -this.amount; 54 } 55 56 public String toString() { 57 return dateOfTransaction+","+naration+","+(credit ? (" 0.0,"+amount) : (amount+", 0.0")); 58 } 59 } 60 61 public void printPassbook() { 62 double runningBalance = 0; 63 System.out.println("Passbook of "+name+" Account # "+ accountNumber); 64 // for (transactionIndex = 0; transactionIndex < nextTransactionIndexInPassbook; transactionIndex++) { 65 for (Object o : passbook) { 66 // t = passbook[transactionIndex]; 67 Transaction t = (Transaction)o; 68 runningBalance += t.getNetAmount(); 69 System.out.println(t+","+runningBalance); 70 } 71 System.out.println("End of Passbook"); 72 } 73 74 public Account(int acno, String n, double openBal) throws NegativeAmountException { 75 /* 76 if (openBal < 0) { 77 throw new NegativeAmountException("Negative Opening Balance", openBal, this); 78 } 79 */ 80 this.accountNumber = acno; 81 this.name = n; 82 // this.balance = openBal; 83 new Transaction("Opening Balance", true, openBal); 84 } 85 86 public int getAccountNumber() { 87 return this.accountNumber; 88 } 89 90 public String getName() { 91 return this.name; 92 } 93 94 public double getBalance() { 95 return this.balance; 96 } 97 98 public void deposit(double amt) throws NegativeAmountException { 99 /* 100 if (amt < 0) { 101 throw new NegativeAmountException("Negative Deposit", amt, this); 102 } 103 */ 104 // this.balance += amt; 105 new Transaction("Deposit", true, amt); 106 } 107 108 public boolean withdraw(double amt) throws NegativeAmountException { 109 /* 110 if (amt < 0) { 111 throw new NegativeAmountException("Negative Withdrawal ", amt, this); 112 } 113 */ 114 if (this.balance < amt) { 115 return false; 116 } 117 // this.balance -= amt; 118 new Transaction("Withdrawal", false, amt); 119 return true; 120 } 121 122 public void display() { 123 // System.out.println("Account:"+this.accountNumber+","+this .name+","+this.balance); 124 System.out.println(this); 125 } 126 127 private static int lastAccountNumber = 1000; 128 129 public Account(String n, double openBal) throws NegativeAmountException { 130 this(++lastAccountNumber, n, openBal); 131 } 132 133 public String toString() { 134 return this.getClass().getName()+":"+this.accountNumber+"," +this.name+","+this.balance; 135 } 136 137 public boolean equals(Object obj) { 138 if (this.getClass() != obj.getClass()) { 139 return false; 140 } 141 return this.accountNumber == ((Account)obj).accountNumber; 142 } 143 144 public int hashCode() { 145 return this.accountNumber; 146 } 147 148 public int compareTo(Account ac) { 149 return this.accountNumber - ac.accountNumber; 150 } 151 }
Map
InterfaceA Map
maintains mapping of a key with a value. The key and value could be any type of object. In Map
, a key is mapped to a single value only, i.e. the keys in a Map
are unique. The various methods of the Map
interface are given in Listing 13.13.
Listing 13.13. Methods in the Map
interface
1 public Object put(Object key, Object value) 2 public Object get(Object key) 3 public Object remove(Object key) 4 public int size() 5 public void clear() 6 public boolean isEmpty() 7 public boolean containsKey(Object key) 8 public boolean containsValue(Object value) 9 public Set keySet() 10 public Collection values() 11 public Set entrySet() 12 public void putAll(Map m)
The put
() method normally creates a new key value pair and returns null. In case the key is already in use then the new value will be mapped to the key and it will return the old value which was previously mapped to that key. The get
() method retrieves the value which was mapped to the key object using a put
() method. In case the key was not mapped, then it would return a null. The remove
() method removes the key value pair (mapping) for the specified key and returns the value which was mapped to the key. So all the three methods put
(), get
() and remove
() return the value which was mapped to the key, but the put and remove method also update the Map
. Similar to the Collection
, here also we have the methods for size returning the number of key value pairs in the Map
. The clear
() method removes all the key value pairs, and isEmpty
() checks if the size is 0. The containsKey
() and containsValue
() methods check if the specified object is used as a key (containsKey
) or as a value (containsValue
) within the Map
. The containsKey
() would be efficient in searching since the key value pairs are maintained using the key object, but the containsValue
() method is not efficient. The keySet
() method would return a Set
containing all the keys used in the Map
. Since the keys are unique, this method returns a Set
. The values
() method returns a Collection
containing all the value objects which have been mapped to the keys within the Map
. This can have duplicates since the same value may have been mapped to more than one key. The method entrySet
() returns a Set
containing elements of the type Map.Entry. The Map
maintains the key value pairs as instances of Map.Entry. An instance of Map.Entry represents a single key value pair, the entrySet
() method returns all the key value pairs as instances of Map.Entry. The Map.Entry is a nested interface in the Map
interface. The methods of the Map.Entry interface are given in Listing 13.14.
Listing 13.14. Methods of Map.Entry
1 public Object getKey() 2 public Object getValue() 3 public Object setValue() 4 public boolean equals(Object o) 5 public int hashCode()
The getKey
() and the getValue
() methods return the key or the value objects in the key/value pair, and the setValue
() method replaces the value object and returns the old value object, which is being replaced. The equals
() method should be overridden, such that two Map.Entry are equal if their keys are equal. The hashCode
() method needs to be overridden to be consistent with the equals
() method.
The putAll
() method is the only bulk operation available in the Map
interface. Using putAll
() method, all the key value pairs from the given Map
are put into the current Map
.
The commonly used concrete implementation classes for the Map
interface are the HashMap
and the Hashtable
classes. Here the Hashtable
class is a legacy class. It is part of the Java API, right from Java 1.0.
The class HashMap
has been derived from the AbstractMap
class. The AbstractMap
class is an abstract class implementing the Map
interface. This class has implementations of some of the methods from the interface, and if we want to create our own implementation of the Map
interface, then this class is a good starting point. Note that the Hashtable
has been modified from Java 1.2 to implement the Map
interface, but this class does not extend from the AbstractMap
class. It still extends from the abstract class Dictionary
. This is done to have backward compatibility of some old code which may be using the Hashtable
instances as Dictionary
. Here it is important to note that the implementation of the Map
interfaces use the hashCode
() method to organize its key value pairs. So, if the implementation of the hashCode
() and equals
() are not consistent for the key objects, then the methods of Map
may not give consistent results, i.e. given any two objects of the key, a
and b
. If a.equals(b) is true
, then a.hashCode() must be equal to b.hashCode(). The consistency requirement for the hashCode
() and equals
() is discussed earlier in Section 8.1.1.
EXERCISE 13.2 Define a class called Bank
, which would maintain all the Account
instances of Exercise 13.1 by their account number using a Map
, where the Integer
account number is key and is mapped to the Account
instance having the same account number. The Bank
class may provide methods for opening a new account, depositing and withdrawing amount into an account, displaying and printing passbook for a given account identified by the account number and also for printing a list of all the accounts in the Bank
.
One of the possible implementations of the Bank
class is given in Listing 13.15 along with an exception class for an invalid account number in Listing 13.16.
Listing 13.15. Bank.java
1 package bank; 2 3 import java.util.Map; 4 import java.util.HashMap; 5 import java.util.Collection; 6 7 public class Bank { 8 9 private String name; 10 private int lastAccountNumber; 11 private Map accountMap = new HashMap(); 12 13 public Bank(String n, int bankCode) { 14 this.name = n; 15 this.lastAccountNumber = bankCode * 10000; 16 } 17 18 public String getName() { 19 return this.name; 20 } 21 22 public int getBankCode() { 23 return this.lastAccountNumber / 10000; 24 } 25 26 public int openSavingsAccount(String n, double openBal) throws NegativeAmountException { 27 Account ac = new SavingsAccount(++lastAccountNumber, n, openBal); 28 accountMap.put(ac.getAccountNumber(), ac); 29 return ac.getAccountNumber(); 30 } 31 32 public int openCurrentAccount(String n, double openBal) throws NegativeAmountException { 33 Account ac = new CurrentAccount(++lastAccountNumber, n, openBal); 34 accountMap.put(ac.getAccountNumber(), ac); 35 return ac.getAccountNumber(); 36 } 37 38 private Account getAccount(int acno) throws NoSuchAccountExecption { 39 Account ac = (Account)accountMap.get(acno); 40 if (ac == null) { 41 throw new NoSuchAccountException(acno); 42 } 43 return ac; 44 } 45 46 public void deposit(int acno, double amt) throws NegativeAmountException, NoSuchAccountException { 47 getAccount(acno).deposit(amt); 48 } 49 50 public boolean withdraw(int acno, double amt) throws NegativeAmountException, NoSuchAccountException { 51 return getAccount(acno).withdraw(amt); 52 } 53 54 public void display(int acno) throws NoSuchAccountException { 55 getAccount(acno).display(); 56 } 57 58 public void printPassbook(int acno) throws NoSuchAccountException { 59 getAccount(acno).printPassbook(); 60 } 61 62 public void listAccounts() { 63 Collection accounts = accountMap.values(); 64 System.out.println("Account list for bank: "+name); 65 for (Object o : accounts) { 66 System.out.println(o); 67 } 68 System.out.println("End of Account list"); 69 } 70 }
Listing 13.16. NoSuchAccountException.java
1 package bank; 2 3 public class NoSuchAccountException extends Exception { 4 5 private int accountNumber; 6 NoSuchAccountException(int acno) { 7 this.accountNumber = acno; 8 } 9 public int getAccountNumber() { 10 return this.accountNumber; 11 } 12 public String toString() { 13 return super.toString() + ":"+this.accountNumber; 14 } 15 }
There are two interfaces which maintain elements in a sorted order. These are the SortedSet
and the SortedMap
interfaces. There are concrete implementation classes for these two interfaces, namely the TreeSet
and the TreeMap
. The TreeSet
maintains all the elements in a sorted order. The logic for ordering the elements is specified normally by using a Comparator
in the constructor of the TreeSet
interface. Here, when we use the Iterator
to iterate over the elements of the SortedSet
, then they will be in the order according to the Comparator
. The working of the Comparator
interface is discussed in Section 10.1.2 in Chapter 10. When we do not specify the Comparator
in the constructor of the HashSet
, then the elements added to the HashSet
must be Comparable
, otherwise the add method would throw a ClassCastException
.
The TreeMap
is a concrete implementation of the SortedMap
interface. The TreeMap
maintains the key value pairs in a sorted order, ordered by the key objects. The keys used in the TreeMap
must be Comparable
or a Comparator
must be specified in the constructor of the TreeMap
.
Collection
FrameworkGenerics is a feature introduced in the Java programming language from Java 5. Generics is about defining classes and interfaces with parameters. The Collection
interface and its subinterfaces like the Set
and List
are all defined with parameters. They are all said to be of generic type. The interface Collection
has been defined as given in Listing 13.17.
Listing 13.17. Collection
with generics
1 package java.util; 2 3 … 4 public interface Collection<E> { 5 public boolean add(E e) 6 … 7 public Iterator<E> iterator() 8 … 9 }
Line 4 in Listing 13.17 shows how an interface or a class can have a declaration of a parameter. The Collection
interface has a type parameter called E
. Whenever the Collection
interface is used as a data type, a reference data type needs to be specified as the value for E
. This is known as type invocation of the type Collection
, e.g. the declarations of
1 Collection<Account> ca; 2 Collection<Transaction> ct; 3 Collection<Rectangle> cr;
are different invocations of the Collection
interface. There is only one interface called Collection
. These invocations are not new interfaces, rather they are different invocations of the same interface Collection
. The type parameter in the generic type is used only by the compiler and is not available at the runtime. This feature is referred to as type erasure at the runtime. Generics is implemented with type erasure at the runtime.
A type parameter in a generic type definition can be used as a data type within the definition, e.g. in Line 5 in Listing 13.17, add
() has a parameter which is declared to be of type E
, which is the type parameter. In Line 7, in the same listing, the type parameter is used in the return type. By using the type parameter as a parameter in a method, we simply ask the compiler to carry out a check on the type of expression used in the method. Let us consider similar methods available in the List
interface.
1 public interface List<E> extends Collection<E> { 2 public void add(int index, E e) 3 public E get(int index) 4 public void set(int index, E e) 5 public ListIterator<E> listIterator() 6 … 7 }
Here the methods like get
() and the ListIterator
() return type uses the type parameter. When the get
() method has a return type which is the type parameter then it helps us in writing a code without using casts. Here we could assign the element directly to the usage of the type parameter without using a cast.
Listing 13.18. Sample code segment showing usage of generics with List
1 List<Transaction> passbook = new ArrayList<Transaction>(); 2 Transaction t = new Transaction(…); // some invocation of constructor 3 passbook.add(t); 4 Transaction t = passbook.get(0); 5 Iterator<Transaction> iter = passbook.iterator(); 6 for (Transaction transaction : passbook) { 7 // processing for each transaction in the passbook 8 }
In Listing 13.18, in Line 4, we have used the get
() method on passbook
and assigned its return value directly (without casting) to the Transaction
type, which is the type parameter for the passbook
.
There are restrictions on where and how the type parameters can be used within the class or interface which has a generic type parameter. The type parameter cannot be used in static
context. The type parameter may be used in any non-static type declaration, method return type, a parameter type declaration, a local variable declaration or a nested type declaration. The type parameter cannot be used for creating instances of the type parameter or an array of the type parameter, i.e. we cannot use new
to create instances of the type parameter or arrays of the type parameter, e.g. new T() or new T[10] is not valid, where T
is a type parameter. The following listing shows the different places where the generic type parameter may be used within a class which has a type parameter.
1 public class GenericClass<A, B> { 2 private A a; // non-static type declaratino 3 public A method() {…} // return type of a method 4 private Collection<A> collectionOfA; // a parameter type declaration 5 public void method1() { 6 A a = null; // local variable 7 } 8 public class NestedClass<A> {…} // nested class declaration 9 }
When we declare a variable of a generic type, we need to specify the type to be substituted for the type parameter, e.g. List<Account> accounts; or List<SavingsAccount> savingsAccounts;. The variable accounts
can refer to any instance which is created as a List
of Account
, e.g. accounts = new ArrayList<Account>(); is a valid assignment. Similarly savingsAccounts = new ArrayList<SavingsAccount>(); is also a valid assignment. Now, we also know that SavingsAccount
is a sub-type of Account
. So, is List<SavingsAccount> a sub-type of List<Account>? Is it possible for us to assign savingsAccounts
to accounts
? This kind of assignment may initially seem to be fine, but if we look at it carefully we find that now, it would be possible to add elements of any type of account into the same list which is referred by the savingsAccounts
variable by invoking the add method on the accounts
instance. So, even though we have Account
being a super-type of SavingsAccount
, List<Account> is not a super-type of List<SavingsAccount>.
When we declare a variable of a generic type, we can use a wild card for the type parameter to indicate that it can refer to any kind of base generic type, i.e. if we declare List<?> anyList; then anyList
is a super-type of any kind of List
. The wild card(?) character represents an unknown type, so anyList
is a List
of unknown. We can indicate bounds on the type parameter by using extends
along with the wild card, e.g. List<? extends
Account> anyAccounts; declares the variable anyAccounts
to refer to any of List
, where the type parameter is a sub-type of Account
. The bound for a generic type can also be specified using the super
keyword, in which case it would mean the type which has the same type as the bound or any of the super-type of the bound, e.g. a declaration of List<? super Number> nList; would mean that the variable nList
would be able to refer to any List
, whose type parameter is a Number
or a super-type of Number
. Figure 13.2 shows the super-type and sub-type relationships between generic types.
Just like we define classes and interfaces with type parameter, even a method or a constructor may also have a type parameter, which is local to that method or constructor. The type parameter has to be declared just before the return type of the method following the modifiers for the method. This type parameter can be used as return type or as parameter type or anywhere locally within the method in which it is declared in a manner similar to the type parameter for the class or interface, e.g. The method toArray
() in the Collection
interface, is declared as follows:
Figure 13.2 Super-type–sub-type relation between generic types
Let us now look at the methods in the collection framework which have been affected by generics.
Collection
Framework Revised for GenericsLet us look at the changes in the Collection
, List
and the Map
interfaces due to generics.
The idea here is that if we have a collection of a particular type, then type checking should be done for the elements being added or replaced in the collection. So, type checking is only required in the methods which would add and replace elements in the collection, and not in the methods which remove elements. Listing 13.19 gives the methods which have been affected by generics; other methods are the same as in Listings 13.3, 13.5 and 13.6.
Listing 13.19. Collection
methods revised for generics
1 public interface Collection<E> extends Iterable<E> { 2 public boolean add(E e); 3 public Iterator<E> iterator(); 4 public <T> T[] toArray(T[] array); 5 public boolean addAll(Collection<? extends E> c); 6 public boolean removeAll(Collection<?> c); 7 public boolean retainAll(Collection<?> c); 8 public boolean containsAll(Collection<?> c); 9 }
In the Collection
interface the generic type checking is done in case of the add
() method. In the iterator
() method the return type is the iterator
of the same type as the type of Collection
. The revised methods of the Iterator
interface are given in Listing 13.20.
Listing 13.20. Iterator
methods revised for generics
1 public interface Iterator<E> { 2 public E next(); 3 public boolena hasNext(); 4 public void remove(); 5 }
The toArray
() method has a local type parameter, which declares the return type as the array of the same type which is passed as parameter. The addAll
() method allows addition of elements from any collection, whose type parameter is the same or a sub-type of the type parameter of the current collection. In case of methods removeAll
(), retainAll
() and containsAll
() there is no restriction on which type of collection is used as a parameter.
Similarly Listing 13.21 lists the methods in the List
interface.
Listing 13.21. List
methods revised for generics
1 public interface List<E> extends Collection<E> { 2 public void add(int index, E e); 3 public E remove(int index); 4 public E get(int index); 5 public E set(int index, E e); 6 public List<E> subList(int fromIndex, int toIndex); 7 public ListIterator<E> listIterator(); 8 public ListIterator<E> listIterator(int index); 9 public boolean addAll(int index, Collection<? extends E> c); 10 }
Listing 13.22 shows the methods of the Map
interface, revised for generics.
Listing 13.22. Map
methods revised for generics
1 public interface Map<K, V> { 2 public V put(K key, V value); 3 public V get(Object o); 4 public V remove(Object 0); 5 public Set<K> keySet(); 6 public Collection<V> values(); 7 public Set<Map.Entry<K, V>> entrySet(); 8 public void putAll(Map<? extends K,? extends V> map); 9 }
The revised Map.Entry
interface is given in Listing 13.23.
Listing 13.23. Map.Entry
methods revised for generics
1 public interface Map.Entry<K, V> { 2 public K getKey(); 3 public V getValue(); 4 public V setValue(V value); 5 }
Iterable
Interface and for-each LoopFrom Java 5, an interface called Iterable
has been introduced. The Collection
interface implements the Iterable
interface. This interface has only one method called iterator
(). This Iterable
interface is also a generic type and has a type parameter. The iterator
() method returns Iterator
of the type parameter of the Collection
. So, in Listing 13.18, in Line 5, we have assigned the return value of iterator
() to Iterator<Transaction>
. We know that from Java 5 onwards, we can use a for-each loop to iterate over elements of an array. This same for-each loop can also be used to iterate over elements of Iterable
, as shown in Lines 6–8 in Listing 13.18.
EXERCISE 13.3 Update the Account
class from Exercise 13.1 and the Bank
class from Exercise 13.2 to use generics when using the List
and Map
.
The Account
class and the Bank
class updated for generics are given in Listings 13.24 and 13.25.
Listing 13.24. Account.java
1 package bank; 2 3 import java.util.Date; 4 import java.util.List; 5 import java.util.ArrayList; 6 7 public abstract class Account implements Comparable<Account> { 8 9 private int accountNumber; 10 private String name; 11 private double balance; 12 13 private List<Transaction> passbook = new ArrayList<Transaction>(); 14 15 public class Transaction { 16 private Date dateOfTransaction = new Date(); 17 private String naration; 18 private boolean credit; 19 private double amount; 20 21 public Transaction(String n, boolean c, double amt) throws NegativeAmountException { 22 if (amt < 0) { 23 throw new NegativeAmountException("Negative "+(c ? "credit" : "debit"), amt, Account.this); 24 } 25 this.naration = n; 26 this.credit = c; 27 this.amount = amt; 28 Account.this.balance += getNetAmount(); 29 passbook.add(this); 30 } 31 32 public Date getDateOfTransaction() { 33 return this.dateOfTransaction; 34 } 35 36 public String getNaration() { 37 return this.naration; 38 } 39 40 public boolean isCredit() { 41 return this.credit; 42 } 43 44 public double getAmount() { 45 return this.amount; 46 } 47 48 public double getNetAmount() { 49 return this.credit ? this.amount : -this.amount; 50 } 51 52 public String toString() { 53 return dateOfTransaction+","+naration+","+(credit ? (" 0.0,"+amount) : (amount+", 0.0")); 54 } 55 } 56 57 public void printPassbook() { 58 double runningBalance = 0; 59 System.out.println("Passbook of "+name+" Account # "+accountNumber); 60 for (Transaction t : passbook) { 61 runningBalance += t.getNetAmount(); 62 System.out.println(t+","+runningBalance); 63 } 64 System.out.println("End of Passbook"); 65 } 66 67 public Account(int acno, String n, double openBal) throws NegativeAmountException { 68 this.accountNumber = acno; 69 this.name = n; 70 new Transaction("Opening Balance", true, openBal); 71 } 72 73 public int getAccountNumber() { 74 return this.accountNumber; 75 } 76 77 public String getName() { 78 return this.name; 79 } 80 81 public double getBalance() { 82 return this.balance; 83 } 84 85 public void deposit(double amt) throws NegativeAmountException { 86 new Transaction("Deposit", true, amt); 87 } 88 89 public boolean withdraw(double amt) throws NegativeAmountException { 90 if (this.balance < amt) { 91 return false; 92 } 93 new Transaction("Withdrawal", false, amt); 94 return true; 95 } 96 97 public void display() { 98 System.out.println(this); 99 } 100 101 private static int lastAccountNumber = 1000; 102 103 public Account(String n, double openBal) throws NegativeAmountException { 104 this(++lastAccountNumber, n, openBal); 105 } 106 107 public String toString() { 108 return this.getClass().getName()+":"+this.accountNumber+"," +this.name+","+this.balance; 109 } 110 111 public boolean equals(Object obj) { 112 if (this.getClass() != obj.getClass()) { 113 return false; 114 } 115 return this.accountNumber == ((Account)obj).accountNumber; 116 } 117 118 public int hashCode() { 119 return this.accountNumber; 120 } 121 122 public int compareTo(Account ac) { 123 return this.accountNumber - ac.accountNumber; 124 } 125 }
Listing 13.25. Bank.java
1 package bank; 2 3 import java.util.Map; 4 import java.util.HashMap; 5 import java.util.Collection; 6 7 public class Bank { 8 9 private String name; 10 private int lastAccountNumber; 11 private Map<Integer, Account> accountMap = new HashMap<Integer, Account>(); 12 13 public Bank(String n, int bankCode) { 14 this.name = n; 15 this.lastAccountNumber = bankCode * 10000; 16 } 17 18 public String getName() { 19 return this.name; 20 } 21 22 public int getBankCode() { 23 return this.lastAccountNumber / 10000; 24 } 25 26 public int openSavingsAccount(String n, double openBal) throws NegativeAmountException { 27 Account ac = new SavingsAccount(++lastAccountNumber, n, openBal); 28 accountMap.put(ac.getAccountNumber(), ac); 29 return ac.getAccountNumber(); 30 } 31 32 public int openCurrentAccount(String n, double openBal) throws NegativeAmountException { 33 Account ac = new CurrentAccount(++lastAccountNumber, n, openBal); 34 accountMap.put(ac.getAccountNumber(), ac); 35 return ac.getAccountNumber(); 36 } 37 38 private Account getAccount(int acno) throws NoSuchAccountException { 39 // Account ac = (Account)accountMap.get(acno); 40 Account ac = accountMap.get(acno); 41 if (ac == null) { 42 throw new NoSuchAccountException(acno); 43 } 44 return ac; 45 } 46 47 public void deposit(int acno, double amt) throws NegativeAmountException, NoSuchAccountException { 48 getAccount(acno).deposit(amt); 49 } 50 51 public boolean withdraw(int acno, double amt) throws NegativeAmountException, NoSuchAccountException { 52 return getAccount(acno).withdraw(amt); 53 } 54 55 public void display(int acno) throws NoSuchAccountException { 56 getAccount(acno).display(); 57 } 58 59 public void printPassbook(int acno) throws NoSuchAccountException { 60 getAccount(acno).printPassbook(); 61 } 62 63 public void listAccounts() { 64 Collection accounts = accountMap.values(); 65 System.out.println("Account list for bank: "+name); 66 // for (Object o : accounts) { 67 for (Account ac : accounts) { 68 System.out.println(ac); 69 } 70 System.out.println("End of Account list"); 71 } 72 }
Collections
CLASSJust like we have the Arrays
class which has all utility methods relating to array handling, the Collections
class contains lots of utility methods related to operations on all types of elements in the collection framework. These are all static
methods and no instance is required to be created in order to use these utility methods. The commonly used methods from the Collections
class are given Listing 13.26.
Listing 13.26. Common methods from Collections
class
1 public static <T> Enumeration<T> enumeration(Collection<T> c) 2 public static <T> void fill(List<? super T> list, T obj) 3 public static int frequency(Collection<?> c, Object o) 4 public static <T> ArrayList<T> list(Enumeration<T> e) 5 public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) 6 public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) 7 public static <T> List<T> nCopies(int n, T o) 8 public static void reverse(List<?> list) 9 public static void rotate(List<?> list, int distance) 10 public static void shuffle(List<?> list) 11 public static <? extends Comparable<? super T>> void sort(List< T> list) 12 public static <T> void sort(List<T> list, Comparator<? super T> c) 13 public static <T> Collection<T> unmodifiableCollection( Collection<? extends T> c) 14 public static <T> List<T> unmodifiableList(List<? extends T> c) 15 public static <T> Set<T> unmodifiableSet(Set<? extends T> c) 16 public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet <? extends T> c) 17 public static <K, V> Map<K, V> unmodifiableMap(Map<? extends K,? extends V> m) 18 public static <K, V> SortedMap<K, V> unmodifiableSortedMap( SortedMap<? extends K,? extends V> m)
StringTokenizer
CLASSThe StringTokenizer
class implements the Enumeration
interface. It is used for iterating over various tokens within a string
identified using a set of delimiting characters. These tokens are available as a String
. The various constructors for the StringTokenizer
are given in Listing 13.27.
Listing 13.27. Constructors for StringTokenizer
1 public StringTokenizer(String s) 2 public StringTokenizer(String s, String delimiters) 3 public StringTokenizer(String s, String delimiters, boolean tokensasdelimiters)
In the first constructor the delimiters are not specified, so initially the default delimiters are considered to be all the white space characters. The second and third constructors have a parameter, where the set of delimiting characters can be specified. In the last constructor, the boolean parameter is used to indicate whether the delimiters are to be skipped or to be available as a separate token for the delimiters. In case it is set to true, then every alternate token will be the String
containing the delimiting characters, which terminated the earlier token. By default, the delimiting characters are skipped and are not provided as a separate token. For e.g. the code given in listing below:
1 String s = "tomcat:x:91:91:Apache Tomcat:/usr/share/tomcat5:/bin/sh"; 2 StringTokenizer st = new StringTokenizer(s, ",: "); 3 while (st.hasMoreTokens()) { 4 System.out.println(st.nextToken()); 5 } 6 System.out.println("output for Delimiters as tokens follows:"); 7 StringTokenizer st = new StringTokenizer(s, ",: ", true); 8 while (st.hasMoreTokens()) { 9 System.out.println(st.nextToken()); 10 }
tomcat
x
91
91
Apache
Tomcat
/usr/share/tomcat5
/bin/sh
output for Delimiters as tokens follows:
tomcat
:
x
:
91
:
91
:
Apache
:
Tomcat
:
/usr/share/tomcat5
:
/bin/sh
The common methods from the StringTokenizer
class are given in Listing 13.28.
Listing 13.28. Methods for StringTokenizer
1 public String nextToken() 2 public boolean hasMoreTokens() 3 public int countTokens() 4 public String nextToken(String delimiters)
Internally a StringTokenizer
maintains the set of delimiting characters and current position within the String
. The nextToken
() is used to get the next token from the current position. It would search for any of the characters specified in the delimiter string and create a string with characters from the current position till the first position from the current position, where any of the characters from the delimiter string are available. This method is overloaded to accept the delimiter string as a parameter. When the delimiter string is given, then the delimiters for the StringTokenizer
instance are first updated and then it looks for the next token. For e.g. the code given below:
1 String s = "bank.SavingsAccount:110110001, your name, 120345.0" 2 StringTokenizer st = new StringTokenizer(s,":"); 3 String firstToken = st.nextToken(); 4 System.out.println(firstToken); 5 String nextToken = st.nextToken(","); 6 System.out.println(nextToken(); 7 while (st.hasMoreElements()) { 8 System.out.println(st.nextToken()); 9 }
would be printed as follows:
bank.SavingsAccount
:110110001
your name
120345.0
The countTokens
() method returns the number tokens still available from this StringTokenizer
using the current setting of the delimiters. This value would decrease as we iterate to obtain next tokens. The hasMoreTokens
() is used to determine whether we have reached the end of the string in which we are looking for tokens. The implementations of nextElement
() and hasMoreElements
() are similar to nextToken
() and hasMoreTokens
() except for the return type being String
in case of nextToken
() and Object
in case of nextElement
().
Pattern
AND Matcher
CLASSESThe full coverage of regular expressions is beyond the scope of this book, and here we are just giving an introduction which can be helpful in getting started with regular expressions. Regular expressions are a kind of template to which several possible strings may match. The regular expression is a way of specifying such a template or a string pattern. A given string could be identified to be following the pattern specified by a regular expression. The pattern of character is specified using special characters, which have their own specific meanings, e.g. the ‘.’ character is used to mean any character. The regular expression x.y
would mean any string which starts with the letter x
followed by any character and then followed by the letter y
. The ‘[’ and ‘]’ characters are used as enclosure to specify a possible set of character values, which could be allowed. So, a regular expression x[a-h]y
would mean any string which starts with the letter x
followed by any one of the letters between a
to h
, followed by the letter y
. Similarly, the characters ‘*’ and ‘+’ are used as quantifiers for the preceding element. The ‘*’ character is used to denote zero or more occurrences of the preceding element and the ‘+’ character is used to denote one or more occurences of the preceding element, e.g. x[a-h]*y
would mean any string which starts with x
may be followed by zero or more occurrences of the letters between a
and h
and them immediately followed by a y
.
Let us say we have an application which accepts command from the user and would need to identify which is the command given by the user and whether it follows the syntax for the command or not. We may define the syntax for the possible commands as follows:
open <type> <name> <open-bal>
where the <type> is either S for savings or C for current
<name> is the name of the user and
<open-bal> may be specified using either whole number or a
decimal value with upto two digits only
deposit <acno> <amount>
where <acno> is the numeric value of account number
<amount> is the value of amount to be deposited using upto
2 decimal places.
withdraw <acno> <amount>
where <acno> is the numeric value of account number
<amount> is the value of amount to be withdrawn using upto
2 decimal places.
display <acno>
where <acno> is the numeric value of account number
passbook <acno>
where <acno> is the numeric value of account number
close <acno>
where <acno> is the numeric value of account number
list
used to list all the accounts
quit
used to quit from the application
The application would like to match and check whether the input command matches the syntax of any of the possible commands and which specific command it matches. For this we may like to use the regular expression to represent the syntax. A regular expression of
([Oo][Pp][Ee][Nn])s+([SsCc])s+(w+)s+(d+(.d1,2))
could be used to match the syntax for the first command given above. Let us try to understand the regular expression by breaking it into smaller parts. In a regular expression the (
and )
are used to specify capturing groups. The initial ([Oo][Pp][Ee][Nn])
is used to match the word open
in either case. The following s+
is used to match one or more white spaces. The following ([SsCc])
is used to capture the type as one of S
or C
in either case. The following s+
is again used to match one or more white spaces. The next group (w+)
would capture the next word. The following s+
is again used to match one or more white spaces. The next group (d+(.d(1,2)))
is used to capture a sequence of digits followed by at the most one group containing a .
followed by one or two digits.
EXERCISE 13.4 Write a regular expression to specify the syntax for the other possible commands given earlier.
The possible regular expressions which could be used are given below:
([Dd][Ee][Pp][Oo][Ss][Ii][Tt])s+(d+)s+(d+(.d1,2))
for
deposit <acno> <amount>
where <acno> is the numeric value of account number
<amount> is the value of amount to be deposited using upto
2 decimal places.
([Ww][Ii][Tt][Hh][Dd][Rr][Aa][Ww])s+(d+)s+(d+(.d1,2))
for
withdraw <acno> <amount>
where <acno> is the numeric value of account number
<amount> is the value of amount to be withdrawn using upto
2 decimal places.
([Dd][Ii][Ss][Pp][Ll][Aa][Yy])s+(d+)
for
display <acno>
where <acno> is the numeric value of account number
([Pp][Aa][Ss][Ss][Bb][Oo][Oo][Kk])s+(d+)
for
passbook <acno>
where <acno> is the numeric value of account number
([Cc][Ll][Oo][Ss][Ee])s+(d+)
for
close <acno>
where <acno> is the numeric value of account number
([Ll][Ii][Ss][Tt])
for
list
([Qq][Uu][Ii][Tt])
for
quit
Pattern
and Matcher
ClassesThe Pattern
and the Matcher
classes belong to the java.util.regex package. The Pattern
class represents the compiled form of a regular expression. Instances of a Pattern
class are immutable. An instance of the Pattern
class can be created by using the static
method compile
() of the Pattern
class. The regular expression represented by an instance of Pattern
may be then matched with an input string using the instance of Matcher
. The instance of Matcher
uses a Pattern
instance and an input character sequence with which the pattern is to be matched. The instance of Matcher
can be created by invoking the matcher
() method on a Pattern
instance.
The common methods of a Pattern
class are given in Listing 13.29.
Listing 13.29. Methods of Pattern
class
1 public static Pattern compile(String regex) 2 public static Pattern compile(String regex, int flags) 3 public String pattern() 4 public Matcher matcher(CharSequence input) 5 public int flags() 6 public static boolean matches(String regex, CharSequnce input)
The static
method matches
() is given as a convenience method where it matches the given regular expression with an input character sequence and returns whether the input sequence matches the specified regular expression. This method in turn compiles the regular expression to the Pattern
instance and then uses the Matcher
instance’s matches
() method. The matches
() method in the Pattern
may be used sparingly. Where a particular regular expression is used multiple times, it is always advisable to create the Pattern
instance and reuse it.
The Matcher
instance is an engine for locating patterns in an input character sequence. The commonly used methods of the Matcher
are given in Listing 13.30.
Listing 13.30. Methods of Matcher
class
1 public Pattern pattern() 2 public Matcher usePattern(Pattern newPattern) 3 public Matcher reset() 4 public Matcher reset(CharSequence input) 5 public boolean matches() 6 public boolean lookingAt() 7 public boolean find() 8 public boolean find(int start) 9 public String group() 10 public int start() 11 public int end() 12 public String group(int group) 13 public int start(int group) 14 public int end(int group)
The pattern
() returns the current pattern
instance being used by the Matcher
. The usePattern
() method is used to change the Pattern
object currently being used by the Matcher
instance. Any group-related information about the previous match on the input character sequence will be lost but maintains the current position in the input character sequence. The reset
() method without any input string resets the current position to 0 and the region is reset to the entire input sequence. Any previous match result will not be available from the group-related methods. The reset
() method with input as parameter resets the current position as well as the input character sequence on which pattern matching or find would be carried out.
The methods matches
(), lookingAt
() and find
() would use the Pattern
instance in the Matcher
and return a boolean
value. The matches
() would return true
only if the entire input character sequence matches the pattern. The loookingAt
() would return true
only if the input character sequence is prefixed by the specified pattern, and the find
() would return true
only if the pattern occurs anywhere within the input character sequence. In all the cases the matching part of the string is identified as a group and the start and end indexes in the input character sequence are updated. These values are available using the group
(), start
() and the end
() methods. The capturing groups within the regular expression are available using the group number of the capturing group in the group
() method. Its corresponding start and the end index values within the input character sequence are also available using the group number in the start
() and the end
() methods.
Scanner
CLASSA Scanner
breaks an input text character into tokens based on delimiters, and then converts them into primitive values or String
. A Scanner
instance is created by specifying some input source. This input source may be a fixed String
or some source of bytes or characters. The sources of bytes and characters are normally instances of InputStream
and Readable
. The InputStream
and Readable
are discussed in Chapter 14. At this stage, we may simply use an instance of InputStream
, System.in, which corresponds to the standard input of the JVM and is available as a static
member of the System
class. We very commonly create instances of Scanner
as given below:
1 Scanner stdScanner = new Scanner(System.in};
This instance of Scanner
reads the input source of bytes from the standard input, converts them into sequence of character according to the default characterset of the native system and then breaks this character sequence into tokens using the delimiter, which by default is the white space characters. The common constructors for the Scanner
class are given in Listing 13.31.
Listing 13.31. Constructors of Scanner
1 public Scanner(InputStream source} 2 public Scanner(InputStream source, String charsetname) 3 public Scanner(File source) 4 public Scanner(File source, String charsetname) 5 public Scanner(Readable source) 6 public Scanner(String source)
The constructors which have byte sources are overloaded to accept another parameter for the charsetname to allow the conversion of the bytes to characters according to the charsetname specified instead of converting from the native characterset.
The Scanner
class maintains a delimiter, which can be specified as a regular expression in the form of a String
or an instance of the Pattern
class. The Scanner
class has lots of nextxxx
() methods. These methods would identify the next token based on the delimiter value from the source characters and then convert these into the appropriate primitive or string according to the method used. When converting to the integral primitive values, the radix value by default is 10, which can be set to any value between 2 and 36. Some of the common methods of the Scanner
class are given in Listing 13.32.
Listing 13.32. Methods of Scanner
class
1 public Pattern delimiter() 2 public Scanner useDelimiter(Pattern pattern) 3 public Scanner useDelimiter(String pattern) 4 public int radix() 5 public Scanner useRadix(int radix) 6 public byte nextByte() 7 public byte nextByte(int radix) 8 public short nextShort() 9 public short nextShort(int radix) 10 public int nextInt() 11 public int nextInt(int radix) 12 public long nextLong() 13 public long nextLong(int radix) 14 public float nextFloat() 15 public double nextDouble() 16 public boolean nextBoolean() 17 public String nextLine() 18 public String next() 19 public String next(Pattern pattern) 20 public String next(String pattern) 21 public BigInteger nextBigInteger() 22 public BigInteger nextBigInteger(int radix) 23 public BigDecimal nextBigDecimal()
The delimiter used by a Scanner
can be got and set by using the delimiter
() and the useDelimiter
() methods. The pattern
() method returns the Pattern
instance which is currently used for identifying end of token and the useDelimiter
() method can take the pattern to be used as delimiter either as an instance of the Pattern
class or as a String
. The radix
() method returns the default radix which would be used while returning the numeric values for the byte
, short
, int
, long
and the BigInteger
. This default value of radix is initially 10, and can be set to any value between 2 and 36 using the useRadix
() method. The next
() method without any parameter simply returns the next token identified using the delimiter, whereas the next
() method with pattern parameter would return the next token as an instance of String
, only if it matches the specified pattern. The pattern to be matched by the token can be specified as an instance of Pattern
or String
. The nextLine
() method ignores the current delimiter setting and returns the next token identified only by using the carriage return or new line character as delimiter. The other nextXXX
() methods identify the token identified using the delimiter and then parses them to the types according to the method and the radix value.
EXERCISE 13.5 Define a class called BankServer
which would use an instance of Scanner
to take commands from the user and would identify the command specified by the user. It should then invoke an appropriate method on the instance of Bank
as defined for Exercise 13.3. The Bank
instance may be initialized in the constructor. The class can provide a method called service
(), which executes a loop where it accepts a command and gives an appropriate response to the command till the quit command is entered by the user.
A possible implementation for the BankServer
class is given in Listing 13.33.
Listing 13.33. BankServer.java
1 package bank; 2 3 import java.util.*; 4 import java.util.regex.*; 5 6 public class BankServer { 7 private Bank bank; 8 private Scanner commandScanner; 9 private static Matcher commandMatcher = Command.QUIT.getSyntax ().matcher("something"); 10 11 public enum Command { 12 OPEN("([Oo][Pp][Ee][Nn])\s+([SsCc])\s+(\w+)\s+(\d +(\.(\d)1,2)?)"), 13 DEPOSIT("([Dd][Ee][Pp][Oo][Ss][Ii][Tt])\s+(\d+)\s+(\d +(\.(\d)1,2)?)"), 14 WITHDRAW("([Ww][Ii][Tt][Hh][Dd][Rr][Aa][Ww])\s+(\d+)\s +(\d+(\.(\d)1,2)?)"), 15 DISPLAY("([Dd][Ii][Ss][Pp][Ll][Aa][Yy])\s+(\d+)"), 16 PASSBOOK("([Pp][Aa][Ss][Ss][Bb][Oo][Oo][Kk])\s+(\d+)"), 17 LIST("([Ll][Ii][Ss][Tt])"), 18 CLOSE("([Cc][Ll][Oo][Ss][Ee])\s+(\d+)"), 19 QUIT("[Qq][Uu][Ii][Tt]"), 20 ; 21 private Pattern syntax; 22 private Command(String pattern) { 23 this.syntax = Pattern.compile(pattern); 24 } 25 public Pattern getSyntax() { 26 return syntax; 27 } 28 public static Command getMatchingCommand(String input) { 29 commandMatcher.reset(input); 30 Command[] allCommands = Command.values(); 31 for (Command command : allCommands) { 32 if (commandMatcher.usePattern(command.getSyntax()). matches()) { 33 return command; 34 } 35 } 36 return null; 37 } 38 } 39 public BankServer(Bank bank) { 40 this.bank = bank; 41 commandScanner = new Scanner(System.in); 42 } 43 public void service() throws NoSuchAccountException, NegativeAmountException { 44 String input = commandScanner.nextLine(); 45 Command command = Command.getMatchingCommand(input); 46 while (command != Command.QUIT) { 47 int acno = 0; 48 double amt = 0; 49 char type = ’s’; 50 String name = ""; 51 52 if (command != null) { 53 switch(command) { 54 case OPEN: 55 type = commandMatcher.group(2).charAt(0); 56 name = commandMatcher.group(3); 57 amt = Double.parseDouble(commandMatcher. group(4)); 58 switch(type) { 59 case ’S’: 60 case ’s’: 61 acno = bank.openSavingsAccount(name , amt); 62 break; 63 case ’C’: 64 case ’c’: 65 acno = bank.openCurrentAccount(name , amt); 66 } 67 bank.display(acno); 68 break; 69 case DEPOSIT: 70 acno = Integer.parseInt(commandMatcher. group(2)); 71 amt = Double.parseDouble(commandMatcher. group(3)); 72 bank.deposit(acno, amt); 73 bank.display(acno); 74 break; 75 case WITHDRAW: 76 acno = Integer.parseInt(commandMatcher. group(2)); 77 amt = Double.parseDouble(commandMatcher. group(3)); 78 if (bank.withdraw(acno, amt)) { 79 bank.display(acno); 80 } else { 81 System.out.println("Warning: Insufficient balance"); 82 } 83 break; 84 case DISPLAY: 85 acno = Integer.parseInt(commandMatcher. group(2)); 86 bank.display(acno); 87 break; 88 case PASSBOOK: 89 acno = Integer.parseInt(commandMatcher. group(2)); 90 bank.printPassbook(acno); 91 break; 92 case LIST: 93 bank.listAccounts(); 94 break; 95 case CLOSE: 96 acno = Integer.parseInt(commandMatcher. group(2)); 97 amt = bank.close(acno); 98 System.out.println(amt+" is the closing balance"); 99 break; 100 } 101 } else { 102 System.out.println("Invalid command"); 103 } 104 input = commandScanner.nextLine(); 105 command = Command.getMatchingCommand(input); 106 } 107 } 108 public static void main(String[] args) throws Exception { 109 String bankName = args[0]; 110 int bankCode = Integer.parseInt(args[1]); 111 Bank bank = new Bank(bankName, bankCode); 112 BankServer server = new BankServer(bank); 113 server.service(); 114 } 115 }
Varargs
AND THE Formatter
CLASSAnyone who has done programming in C
would have come across the printf
() function. How many parameters does it have? The number of parameters for the printf
() function is not fixed in C
. It can be invoked by using variable number of arguments. From Java 5, we can have methods in Java with variable number of arguments. In fact, the printf
() method is available in Java from Java 5 onwards. When we define a method, then only for the last argument, we may specify that there would be variable number of arguments of the specified type, e.g. the printf
() method available in the PrintStream
class (System.out is of type PrintStream
) has been defined as given below:
1 public void printf(String format, Object… values)
The last parameter has been declared as Object…
. The elipses following the Object
indicate that there can be any number of Object
arguments following the first String
argument, which may be used in the method invocation. Within the method all the values of the last parameter are available as an array. There can be zero or more occurences of the last parameter type, when the parameter is specified with elipses. The variable arguments cannot be specified for primitive types and they cannot be used for any parameter other than the last parameter. The printf
() method of the PrintStream
and PrintWriter
does formatting exactly as per the rules for the format
() method of the Formatter
class.
Formatter
ClassThe Formatter
class is used to achieve C
’s printf
() style formatting for various data types. The format
() method of the Formatter
class directly sends the formatted output to some destination. The various constructors require parameters which are either an Appendable
or can be used to create an Appendable
. The formatting is based on a format string which is used to specify the alignment and justification for common data types like the numeric data, date and time-related data and string data. The data types include the classes like the BigDecimal
and Calendar
. Some of the common constructors for the Formatter
class are given in Listing 13.34.
Listing 13.34. Constructors for Formatter
1 public Formatter() 2 public Formatter(Appendable destination) 3 public Formatter(String filename) 4 public Formatter(String filename, String charactersetname) 5 public Formatter(OutputStream out) 6 public Formatter(OutputStream out, String charactersetname)
The default constructor creates an instance of StringBuilder
where the formatted output will be stored. This StringBuilder
instance can be accessed using the out
() method. The common methods of the Formatter
class are given in Listing 13.35.
Listing 13.35. Methods of Formatter
1 public Formatter format(String formatString, Object… args) 2 public Appendable out() 3 public void flush() 4 public void close() 5 public String toString()
The format
() method takes the first parameter as a String
and is then followed by a variable number of arguments of any reference types. Because of the boxing conversions, even primitive types are also allowed to be used as the parameter. The format specifier in the first parameter is similar to the format specifier used in the printf
() function of C
. The printf
() method of PrintStream
and the PrintWriter
classes work exactly like the format
() method of the Formatter
. There are some differences in the format specification for C
language and the format specification used by the format
() and the printf
() methods in Java. The details of the format specification are available from the API documentation for the Formatter
class.
EXERCISE 13.6 Update the printPassbook
() method in the Account
class of 13.3 to format the date of transaction using the format
() method.
One of the possible implementations for the printPassbook
() with formatting can be as given in Listing 13.36.
Listing 13.36. printPassbook
() method with formatting
1 public void printPassbook() { 2 double runningBalance = 0; 3 System.out.println("Passbook of "+name+" Account # "+accountNumer); 4 for (Transaction t : passbook) { 5 runningBalance += t.getNetAmount(); 6 System.out.printf("%1$te-%1$tb-%1$tY,%2$15s,%3$10.2f,%4 $10.2f,%5$10.2f ", 7 t.getDateOfTransaction(), 8 t.getNaration(), 9 t.isCredit()?0:t.getAmount(), 10 t.isCredit()?t.getAmount():0, 11 runningBalance); 12 } 13 System.out.println("End of Passbook"); 14 }
Date
, Calendar
class and its sub-classes are normally used for managing date and time.Arrays
class contains methods for most of the common operations required on all kinds of arrays.Set
interface ensure unique (based on equals
() method) collection of elements.Collections
class contains methods for most of the common operations required for the collection framework.StringTokenizer
. The string parsing based on regular expressions is also supported with the help of Pattern
and the Matcher
classes.Scanner
class can be used for parsing input from a stream. This stream is normally a character stream. It has standard methods for parsing standard data types from the stream and supports the use of a regular expression for parsing the input based on a pattern.Formatter
class. The format
() method in the Formatter
class supports the formatting of various kinds of data types similar to the printf
function of the C
language.CurrentAccount
is a sub-class of Account
, List<CurrentAccount>
is a sub-type of List<Account>
.nextToken()
method of StringTokenizer
returns value of type __________.Date
class, time in milliseconds is maintained relative to __________ GMT.1 import java.util.*; 2 3 public class Test { 4 public static void main(String[] args) { 5 StringTokenizer st = new StringTokenizer("Hello:abc, xyz, 123",":"); 6 System.out.println(st.countTokens()); 7 String s = st.nextToken(); 8 System.out.println(st.countTokens()); 9 s = st.nextToken(","); 10 System.out.println(st.countTokens()); 11 } 12 }
1 import java.util.*; 2 3 public class Test { 4 public static void main(String[] args) { 5 String inputString = "abc, xyz, mno, pqr"; 6 StringTokenizer st = new StringTokenizer(inputString," ,", true); 7 System.out.println(st.countTokens()); 8 } 9 }
java.util.List
interface extends the java.util.Collection
interface. Which of the following methods are in the List
interface but not found in the Collection
?
public void set(Object element, int index)
public boolean addAll(int index, Collection c)
public void set(int index, Object element)
public Iterator iterator()
public Object get(int index)
java.util.Set
interface extends the java.util.Collection
interface. Which of the following methods are in the Set
interface not found in the Collection
?
public boolean contains(Object element)
public boolean addAll(Collection c)
public Collection intersection(Collection c)
public Iterator iterator()
1 import java.util.Scanner; 2 3 public class TestScanner { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner("7,8,9,10"); 6 scanner.useDelimiter(",").useRadix(8); 7 System.out.println(scanner.nextInt()); 8 System.out.println(scanner.nextInt()); 9 System.out.println(scanner.nextInt()); 10 System.out.println(scanner.nextInt()); 11 } 12 }
What is the output of compiling and executing the code above?
StringTokenizer
and outputs the tokens in reverse order.3.145.86.211