CS 1301 * Ch 6, Handout 1

advertisement
CS 1302 – Ch 22, Java Collections Framework – Part 1
Sections
1-11
Pages
728-756
Review Questions
Programming Exercises
Some code accompanies these notes and are found on the Schedule.
Sections 22.1 – Introduction
1. Frequently, we find it useful to group related objects together, to form a collection of
objects/elements. A collection is implemented by a data structure. A data structure provides a way to
add items to the collection and to access items. Sometimes we refer to a collection as a collection class
or a container class. The JCF defines a root interface, Collection. There are three major types of
collections in Java. Each of these is an interface and each is a sub-interface of Collection.
a. List – Most commonly used. A sequentially ordered set of objects. Can access values based on their
position. Examples: A list of orders or items in a warehouse, a mailing list.
b. Set – No duplicates allowed. No order on elements is imposed. No random access. Examples: a
student’s set of courses for a semester, a set of passwords for registered users.
c. Queue – Stores objects that are processed in a first-in, first-out (FIFO) fashion.
2. There are four common implementations of Lists:
a. ArrayList – Backed by arrays. When size gets to large, array size is doubled by creating a new array
and copying. Arrays in Java occupy contiguous memory. Not synchronized. Faster than Vector for
single-threaded applications. Add, remove, contains are O(n). Positional access is constant.
Example: Books checked out at a library by a patron.
b. Vector – Backed by an array. Synchronized, for use with multithreaded applications.
c. Stack – A subclass of Vector used to implement a stack.
d. LinkedList – Uses dynamic memory to allocate space for each object. Constant time add and
remove. Contains is O(n). Positional access is O(n).
3. There are three common implementations of Sets:
a. HashSet – uses a hash table to store values. Add, remove, contains, all are constant time.
b. LinkedHashSet – Somewhere in between.
c. TreeSet – uses a tree to store values. Add, remove, contains are O(log(n)).
When to use: All objects unique. Don’t need random access. If you need sorted order, use TreeSet. If
you need to use the input order, use LinkedHashSet. Use HashSet when order is not important.
4. There is one common implementation of Queue – the PriorityQueue.
5. Another type of collection is a Map – A map stores values which are accessed by keys. Each value is
associated with a key. Thus, we say a Map stores key/value pairs. We can use a key to quickly search
for an object. No duplicate keys are allowed because keys are stored in a set. Values are stored in a list.
1
Example: a dictionary – the key is the word and the value is the definition; the health records for
patients in a doctor’s office – the key is the patient and the value is the associated health records.
A Map is an interface, but is not a sub-interface of Collection.
6. There are three common implementations of Maps:
a. HashMap
b. LinkedHashMap
c. TreeMap
When to use: A unique key identifies each object. Need random access. The difference in the three is in
the ordering of their keys.
Sections 22.2 – Collections
1. Java groups all collection classes into the Java Collections Framework (JCF). You can learn more about
this at:
http://java.sun.com/docs/books/tutorial/collections/index.html
http://java.sun.com/docs/books/tutorial/collections/implementations/index.html
2. Consider the Collection interface which is the root interface for all collection classes. It specifies the
minimum services any collection class must supply.
«interface»
java.util.Collection<E>
+add(o: E): boolean
Adds a new element o to this collection.
+addAll(c: Collection<? extends E>): boolean Adds all the elements in the collection c to this collection.
+clear(): void
Removes all the elements from this collection.
+contains(o: Object): boolean
Returns true if this collection contains the element o.
+containsAll(c: Collection<?>):boolean
Returns true if this collection contains all the elements in c.
+equals(o: Object): boolean
Returns true if this collection is equal to another collection o.
+hashCode(): int
Returns the hash code for this collection.
+isEmpty(): boolean
Returns true if this collection contains no elements.
+iterator(): Iterator
Returns an iterator for the elements in this collection.
+remove(o: Object): boolean
Removes the element o from this collection.
+removeAll(c: Collection<?>): boolean
Removes all the elements in c from this collection.
+retainAll(c: Collection<?>): boolean
Retains the elements that are both in c and in this collection.
+size(): int
Returns the number of elements in this collection.
+toArray(): Object[]
Returns an array of Object for the elements in this collection.
«interface»
java.util.Iterator<E>
+hasNext(): boolean
Returns true if this iterator has more elements to traverse.
+next(): E
Returns the next element from this iterator.
+remove(): void
Removes the last element obtained using the next method.
2
3. Now, consider the Java Collections Framework (JCF) hierarchy. We will consider each of the 11
concrete classes shown.
SortedSet
TreeSet
Set
AbstractSet
Collection
HashSet
LinkedHashSet
AbstractCollection
Vector
Stack
AbstractList
List
ArrayList
AbstractSequentialList
LinkedList
Deque
AbstractQueue
Queue
Concrete Classes
Abstract Classes
Interfaces
PriorityQueue
Note that the Map interface (below) is not a collection. However, the keys are stored in a Set and the
values are stored in a List and is a part of the JCF.
SortedMap
Map
TreeMap
AbstractMap
Interfaces
Abstract Classes
HashMap
LinkedHashMap
Concrete Classes
4. Below, we shown another view of the hierarchy. Here, we just show the three major sub interfaces
(List, Set, Queue) and some of the concrete implementations. The abstract classes are not shown. This
view shows how the behaviors are propagated down the hierarchy. The abstract classes implement so
of the behaviors.
3
4
5. Here, we show the Iterator interfaces and the
Map interfaces:
5
Sections 22.3 – The Collections Interface and the AbstractCollection Class
1. See item 2 in the previous section.
Sections 22.4 – Sets
1. The Set interface extends the Collection interface. It does not introduce new methods or constants, but
it stipulates that an instance of Set contains no duplicate elements. The concrete classes that
implement Set must ensure that no duplicate elements can be added to the set. That is, no two
elements e1 and e2 can be in the set such that e1.equals(e2) is true.
2. The Set interface hierarchy
«interface»
java.util.Collection<E>
«interface»
java.util.Set<E>
java.util.AbstractSet<E>
«interface»
java.util.SortedSet<E>
+first(): E
java.util.HashSet<E>
+HashSet()
+last(): E
+HashSet(c: Collection<? extends E>)
+headSet(toElement: E): SortedSet<E>
+HashSet(initialCapacity: int)
+tailSet(fromElement: E): SortedSet<E>
+HashSet(initialCapacity: int, loadFactor: float)
«interface»
java.util.NavigableSet<E>
java.util.LinkedHashSet<E>
+pollFirst(): E
+LinkedHashSet()
+pollLast(): E
+LinkedHashSet(c: Collection<? extends E>)
+lower(e: E): E
+LinkedHashSet(initialCapacity: int)
+higher(e: E):E
+LinkedHashSet(initialCapacity: int, loadFactor: float)
+floor(e: E): E
+ceiling(e: E): E
java.util.TreeSet<E>
+TreeSet()
+TreeSet(c: Collection<? extends E>)
+TreeSet(comparator: Comparator<? super E>)
+TreeSet(s: SortedSet<E>)
6
3. The AbstractSet class is a convenience class that extends AbstractCollection and implements Set. The
AbstractSet class provides concrete implementations for the equals method and the hashCode
method. The hash code of a set is the sum of the hash code of all the elements in the set. We will
consider this later. Since the size method and iterator method are not implemented in the AbstractSet
class, AbstractSet is an abstract class.
Sections 22.4.1 – The HashSet Class
1. The HashSet class is a concrete class that implements Set. It can be used to store duplicate-free
elements. For efficiency, objects added to a hash set need to implement the hashCode method in a
manner that properly disperses the hash code.
2. Create a HashSet of Strings. Notice that we use Set as the reference type. We could have used HashSet
but we prefer to be as general as possible.
// Create a hash set
Set<String> set = new HashSet<String>();
3. Add some strings:
Code
System.out.print(
System.out.print(
System.out.print(
System.out.print(
System.out.print(
Output
true
true
true
true
true
set.add("London") );
set.add("Paris") );
set.add("New York") );
set.add("San Francisco") );
set.add("Beijing") );
Try to add a duplicate:
Code
System.out.println( set.add("New York") );
Output
false
4. Print the set. Set has a nice toString method. Notice that the order is not the order the items were
added. We cannot guarantee the order with HashSet.
Code
System.out.println( set );
Output
[San Francisco, New York, Paris, Beijing, London]
5. Remove elements from set:
Code
// Try removing;
System.out.print( set.remove("Paris") );
System.out.println( set.remove("Chicago") );
// Print set
System.out.println( set );
7
Output
true
false
[San Francisco, New York, Beijing,
London]
6. Search for an item in set. Notice that search doesn’t return the item, it just tells whether it is in the set
or not.
Code
System.out.print( set.contains( "Beijing" ) + ", " );
System.out.println( set.contains( "Moab" ) );
Output
true
false
7. Try to change the elements in a set using the enhanced for loop
Code
for( String c : set )
{
c += "-a";
System.out.print( c + ", " );
}
System.out.println();
// Print set
System.out.println( set );
Output
San Francisco-a, New York-a, Beijing-a,
London-a,
[San Francisco, New York, Beijing, London]
8. Use the iterator method. Can remove items from set, but can’t modify them.
Code
Iterator<String> iterator = set.iterator();
while ( iterator.hasNext() )
{
String s = iterator.next();
// Can remove things
if( s.equals("New York") )
iterator.remove();
// but can't modify
s += "-b";
}
System.out.println( set );
Output
[San Francisco, Beijing, London]
9. Turn set into an array. We modify the items in the array, but the set is untouched. A copy is made. But,
remember that String behaves like a primitive type. If we had custom objects, we’d need to be more
careful.
Code
String[] ary = new String[ set.size() ];
ary = set.toArray( ary );
Output
// Modify elements.
for( int i=0; i<ary.length; i++ )
ary[i] += "-b";
// Print array
System.out.println(
Arrays.toString(ary) );
// Print set
System.out.println( set);
[San Francisco-b, Beijing-b, London-b]
[San Francisco, Beijing, London]
8
10. Create a new set from the array. First, we convert the array to a list using the Arrays class utility
method, asList. Then, the HashSet (and in fact any Collection) has an overloaded constructor that takes
a Collection to initialize the set. If there were duplicates, they would be removed.
Code
List<String> list = Arrays.asList( ary );
Set<String> set2 = new HashSet<String>( list );
// Print set
System.out.println( set2 );
Output
[Beijing-b, San Francisco-b,
London-b]
Sections 22.Extra – HashSet & HashMap with Custom Objects
1. If you want to use HashSet with custom objects then the custom class must override both the equals
and hashCode methods. The Java API says that if two objects are to be considered equal
(a.equals(b)==true) then their hashcodes must be equal (a.hashCode()==b.hashCode()). Note, this does
not say that two objects that have the same hashcode are equal. The signature for the two methods
are:
public boolean equals( Object o )
public int hashCode()
We already know what the equals method is used to enforce logical equality. For instance, two
Employees are equal if and only if they have the same first name, last name, and SSN.
2. The hashCode method is a bit more involved to explain. You will study hashing in detail in the next
course (CS 3410). For now, we say that the hashCode method returns an integer that represents the
location of the object. It is a short-cut way to find an object in a set, however, its use is encapsulated
(hidden) inside the HashSet class. The operation works something like this: in the course of doing
certain operations (add, contains) if the HashSet class determines that two objects have the same
hashcode, then it calls equals to see if the objects are really the same.
3. For instance, suppose we are using a HashSet to hold Employee objects. When we add an Employee to
the HashSet, the Employee’s hashcode is used to store the object, like an index in an array. For
example, an Employee object may have a hashcode of 48, so the HashSet add method would put this
object in the 48th position in an array. Later, when you want to call the contains or remove method
with that Employee object, the code retrieves the hashcode (48) for the object and then finds the
object in position 48. The situation is identical with HashMap if we want to use custom objects for the
keys or if we want to use the containsValue method when the values are custom objects.
4. So, how do we override the hashcode method? We will show a simple approach here and again in the
HashMap section. You will consider this in more detail in CS 3410.
9
5. Example – The class below only implements the equals method, but not the hashCode method.
Code – Incorrect, only overrides the equals method.
class EmployeeIncorrect
{
private String lName;
private String fName;
public EmployeeIncorrect( String lName, String fName )
{
this.lName = lName;
this.fName = fName;
}
public String getLastName() { return lName; }
public String getFirstName() { return fName; }
public boolean equals(Object o)
{
EmployeeIncorrect e = (EmployeeIncorrect)o;
return ( this.getLastName().equals(e.getLastName()) ) &&
( this.getFirstName().equals(e.getFirstName()) );
}
}
So, we create two Employees that are equal, but they both get added to the set. This is because the
add method calls the hashCode method on each object and they are both different, so equals never
gets called. The hashCode method that is called is the one implemented in the Object class which
usually uses the address of the object to compute its hashcode. Usually this unique.
Code
Set<EmployeeIncorrect> set = new HashSet<EmployeeIncorrect>();
Output
EmployeeIncorrect e1 = new EmployeeIncorrect("Jones", "Hugh" );
EmployeeIncorrect e2 = new EmployeeIncorrect("Jones", "Hugh" );
System.out.println( set.add( e1 ) );
System.out.println( set.add( e2 ) );
true
true
System.out.println( set );
[Hugh Jones,
Hugh Jones]
10
6. Example – Here, we also override the hashCode method in the Employee class.
Code
class Employee
{
private String lName;
private String fName;
public Employee( String lName, String fName )
{
this.lName = lName;
this.fName = fName;
}
public String getLastName() { return lName; }
public String getFirstName() { return fName; }
public String toString()
{
return String.format("%s %s", getFirstName(), getLastName() );
}
public boolean equals(Object o)
{
Employee e = (Employee)o;
return ( this.getLastName().equals(e.getLastName()) ) &&
( this.getFirstName().equals(e.getFirstName()) );
}
public int hashCode()
{
int x = lName.hashCode();
int y = fName.hashCode();
int z = 23*x + 7*y;
return z;
}
}
Some test code now shows that the duplicate object will not be added
Code
Set<Employee> set2 = new HashSet<Employee>();
Output
Employee e3 = new Employee("Jones", "Hugh" );
Employee e4 = new Employee("Jones", "Hugh" );
System.out.println( set2.add( e3 ) );
System.out.println( set2.add( e4 ) );
true
false
System.out.println( set2 );
[Hugh Jones]
11
Sections 22.4.2 – The LinkedHashSet Class
1. The LinkedHashSet class is virtually identical to HashSet except that the order is preserved.
Code
Set<String> set = new LinkedHashSet<String>();
Output
// Add some cities
System.out.print( set.add("London") );
System.out.print( set.add("Paris") );
System.out.print( set.add("New York") );
System.out.print( set.add("San Francisco") );
System.out.print( set.add("Beijing") );
System.out.println( set.add("New York") );
true
true
true
true
true
false
// Print set
System.out.println( set );
[London, Paris, New York, San
Francisco, Beijing]
Sections 22.4.3 – The TreeSet Class
1. SortedSet is a subinterface of Set, which guarantees that the elements in the set are sorted. TreeSet is
a concrete class that implements the SortedSet interface. You can use an iterator to traverse the
elements in the sorted order. The elements can be sorted in two ways.
a. One way is to use the Comparable interface. Thus, to create a TreeSet of custom objects, the class
must implement Comparable.
b. The other way is to specify a Comparator for the elements in the set. Instead of putting the
comparison in the class itself, as with implementing Comparable, put the comparison code in a
separate class, a Comparator. This approach is referred to as order by comparator.
2. Example – Create a HashSet, add some items and then create a TreeSet from HashSet.
Code
// Create a hash set
Set<String> set = new HashSet<String>();
Output
// Add strings to the set
set.add("London");
set.add("Paris");
set.add("New York");
set.add("New Brunswick");
set.add("San Francisco");
set.add("Beijing");
set.add("New York");
// Create TreeSet from HashSet
TreeSet<String> treeSet =
new TreeSet<String>(set);
// Print TreeSet
System.out.println( treeSet );
[Beijing, London, New Brunswick, New
York, Paris, San Francisco]
12
3. Example (continued) – Use SortedSet methods. Initial TreeSet (from above):
[Beijing, London, New Brunswick, New York, Paris, San Francisco]
Code
// Returns the first (lowest) element currently in this set.
System.out.println(treeSet.first());
// Returns the last (highest) element currently in this set.
System.out.println(treeSet.last());
Code
// Returns a view of the portion of this set
// whose elements // are strictly less than
// toElement, {x|x<e}
System.out.println(treeSet.headSet("New York"));
// Returns a view of the portion of this set
// whose elements // are greater than or equal
// to fromElement, {x|x>=e}
System.out.println(treeSet.tailSet("New York"));
System.out.println(treeSet.tailSet("New"));
// Returns a view of the portion of this set
// whose elements // range from fromElement,
// inclusive, to toElement, exclusive. x|e1<=x<e2}
System.out.println(treeSet.subSet("London",
"New York"));
Output
Beijing
San Francisco
Output
[Beijing, London, New
Brunswick]
[New York, Paris, San
Francisco]
[New Brunswick, New York,
Paris, San Francisco]
[London, New Brunswick]
4. Example (continued) – Use NavigableSet methods. Initial TreeSet:
[Beijing, London, New Brunswick, New York, Paris, San Francisco]
Code
// Returns the greatest element in this set strictly less
// than the given element, or null if there is no such
// element, x<e
System.out.println(treeSet.lower("P"));
// Returns the least element in this set strictly greater
// than the given element, or null if there is no such
// element, x>e
System.out.println(treeSet.higher("P"));
System.out.println(treeSet.higher("Paris"));
Code
// Returns the greatest element in this set less than or
// equal to the given element, or null if there is no such
// element, x<=e
System.out.println(treeSet.floor("P"));
System.out.println(treeSet.floor("Paris"));
// Returns the least element in this set greater than or
// equal to the given element, or null if there is no such
// element, x>=e
System.out.println(treeSet.ceiling("P"));
System.out.println(treeSet.ceiling("Paris"));
13
Output
New York
Paris
San Francisco
Output
New York
Paris
Paris
Paris
Code
// Retrieves and removes the first (lowest) element, or
// returns null if this set is empty.
System.out.println(treeSet.pollFirst());
// Retrieves and removes the last (highest) element, or
// returns null if this set is empty.
System.out.println(treeSet.pollLast());
Output
System.out.println(treeSet);
[London, New Brunswick,
New York, Paris]
Code
Iterator<String> i = treeSet.iterator();
while( i.hasNext() )
System.out.print( i.next());
// Reverse Iterator
i = treeSet.descendingIterator();
while( i.hasNext() )
System.out.print( i.next());
Beijing
San Francisco
Output
London, New Brunswick, New
York, Paris,
Paris, New York, New
Brunswick, London,
5. Example 2 – Custom class that implements Comparable. First, consider the Employee class that
implements Comparable by comparing Employees based on last name, and then first name.
class Employee implements Comparable<Employee>
{
private String lName;
private String fName;
private int ssNum;
private double salary;
public Employee(String lName, String fName, int ssNum, double salary)
{
this.lName = lName; this.fName = fName; this.ssNum = ssNum;
this.salary = salary;
}
public
public
public
public
public
String getLastName() { return lName; }
String getFirstName() { return fName; }
int getSSNum() { return ssNum; }
double getSalary() { return salary; }
void setSalary( double salary ) { this.salary = salary; }
public String toString() { ... }
public int compareTo( Employee e )
{
int lName = this.getLastName().compareTo(e.getLastName());
if( lName != 0 )
return lName;
else
return this.getFirstName().compareTo(e.getFirstName());
}
}
14
6. Example 2 (continued) – Some driver code. Note that e7 is not added because the compareTo method
says it is the same as e4. The set is of course ordered on last name, first name.
Code
// Create a treeset with no comparator.
TreeSet<Employee> tsEmployees =
new TreeSet<Employee>();
Output
Employee e1, e2, e3, e4, e5, e6, e7;
e1 = new Employee("Lyton", "Xavier",
243558673, 77.88);
e2 = new Employee("Dern", "Donald",
476227851, 23.44);
e3 = new Employee("Lyton", "Nelly",
338290448, 82.45);
e4 = new Employee("Reilly", "Junita",
338290448, 82.45);
e5 = new Employee("Colter", "Ben",
883456321, 12.89);
e6 = new Employee("Mixton", "Zerby",
422789012, 22.76);
e7 = new Employee("Reilly", "Junita",
126270942, 82.45);
// Put students in treeset
tsEmployees.add(e1);
tsEmployees.add(e2);
tsEmployees.add(e3);
tsEmployees.add(e4);
tsEmployees.add(e5);
tsEmployees.add(e6);
false
System.out.println( tsEmployees.add(e7) );
[(Colter, Ben - 883456321, 12.89),
(Dern, Donald - 476227851, 23.44),
(Lyton, Nelly - 338290448, 82.45),
(Lyton, Xavier - 243558673, 77.88),
(Mixton, Zerby - 422789012, 22.76),
(Reilly, Junita - 338290448, 82.45)]
System.out.println( tsEmployees );
Sections 22.5 – The Comparator Interface
1. You can use a Comparator to create a TreeSet based on some other ordering of the objects. For
instance, the Employee class above implements Comparable to provide a way to sort objects based on
last name and first name. Suppose we want to create a TreeSet of Employees sorted on SSN. We could
create a Comparator like this:
class EmployeeSSNumComparator implements Comparator<Employee>
{
public int compare( Employee e1, Employee e2 )
{
return e1.getSSNum() - e2.getSSNum();
}
}
Notice the Comparator interface requires a compare method and uses generics to specify the objects
to compare. It also requires an equals method, but one is inherited, so it is not necessary, here, to
override.
15
2. Next, we can use a TreeSet constructor that takes a Comparator.
EmployeeSSNumComparator comp = new EmployeeSSNumComparator();
TreeSet<Employee> tsEmployees = new TreeSet<Employee>(comp);
This TreeSet will then use this Comparator to order elements, not the compareTo method in the
Employee class.
3. Some Driver code:
Code
e1 = new Employee("Dern", "Donald",
476227851, 23.44);
e2 = new Employee("Lyton", "Xavier",
243558673, 77.88);
e3 = new Employee("Lyton", "Nelly",
338290448, 82.45);
Output
tsEmployees.add(e1);
tsEmployees.add(e2);
tsEmployees.add(e3);
[(Lyton, Xavier - 243558673, 77.88),
(Lyton, Nelly - 338290448, 82.45),
(Dern, Donald - 476227851, 23.44)]
System.out.println( tsEmployees );
4. Suppose we want to remove an Employee and we only know the SSN. We can create a dummy
employee object with the SSN and made-up values for name and salary.
Code
Employee dummy = new Employee(
"Doo", "Daa", 338290448, 0.00);
Output
System.out.println(
tsEmployees.remove(dummy));
true
System.out.println( tsEmployees );
[(Lyton, Xavier - 243558673, 77.88),
(Dern, Donald - 476227851, 23.44)]
16
5. The text provides an example where you can use a Comparator to compare “different” types of
objects. There, they consider the GeometricObject class, and two subclasses, Circle and Rectangle.
There, they define a GeometricObjectComparator that compares two GeometricObjects based on their
area.
import java.util.Comparator;
public class GeometricObjectComparator implements Comparator<GeometricObject>
{
public int compare(GeometricObject o1, GeometricObject o2)
{
double area1 = o1.getArea();
double area2 = o2.getArea();
if (area1 < area2)
return -1;
else if (area1 == area2)
return 0;
else
return 1;
}
}
and then some driver code:
Set<GeometricObject> set =
new TreeSet<GeometricObject>(new GeometricObjectComparator());
set.add(new Rectangle(4, 5));
set.add(new Circle(40));
set.add(new Circle(40));
set.add(new Rectangle(4, 1));
Sections 22.6 – Lists
1. A set stores non-duplicate elements. To allow duplicate elements to be stored in a collection, you need
to use a list. A list can not only store duplicate elements, but can also allow the user to specify where
the element is stored. The user can access the element by index.
17
2. The List interface hierarchy
«interface»
java.util.Collection<E>
«interface»
java.util.List<E>
+add(index: int, element:E): boolean
Adds a new element at the specified index.
+addAll(index: int, c: Collection<? extends E>)
: boolean
Adds all the elements in c to this list at the specified
index.
+get(index: int): E
Returns the element in this list at the specified index.
+indexOf(element: Object): int
Returns the index of the first matching element.
+lastIndexOf(element: Object): int
Returns the index of the last matching element.
+listIterator(): ListIterator<E>
Returns the list iterator for the elements in this list.
+listIterator(startIndex: int): ListIterator<E>
Returns the iterator for the elements from startIndex.
+remove(index: int): E
Removes the element at the specified index.
+set(index: int, element: E): E
Sets the element at the specified index.
+subList(fromIndex: int, toIndex: int): List<E>
Returns a sublist from fromIndex to toIndex.
3. The ListIterator Interface extends the Iterator interface. Thus, in addition to the Iterator methods, a
ListIterator also provides these methods.
«interface»
java.util.Iterator<E>
«interface»
java.util.ListIterator<E>
+add(o: E): void
Adds the specified object to the list.
+hasPrevious(): boolean
Returns true if this list iterator has more elements
when traversing backward.
+nextIndex(): int
Returns the index of the next element.
+previous(): E
Returns the previous element in this list iterator.
+previousIndex(): int
Returns the index of the previous element.
+set(o: E): void
Replaces the last element returned by the previous or
next method with the specified element.
18
Sections 22.6.1 – The ArrayList and LinkedList Classes
1. The ArrayList class and the LinkedList class are concrete implementations of the List interface. Which of
the two classes you use depends on your specific needs. If you need to support random access through
an index without inserting or removing elements from any place other than the end, ArrayList offers
the most efficient collection. If, however, your application requires the insertion or deletion of
elements from any place in the list, you should choose LinkedList. A list can grow or shrink dynamically.
An array is fixed once it is created. If your application does not require insertion or deletion of
elements, the most efficient data structure is the array.
2. The ArrayList & LinkedList classes.
«interface»
java.util.Collection<E>
«interface»
java.util.List<E>
java.util.ArrayList<E>
+ArrayList()
Creates an empty list with the default initial capacity.
+ArrayList(c: Collection<? extends E>)
Creates an array list from an existing collection.
+ArrayList(initialCapacity: int)
Creates an empty list with the specified initial capacity.
+trimToSize(): void
Trims the capacity of this ArrayList instance to be the
list's current size.
19
«interface»
java.util.Collection<E>
«interface»
java.util.List<E>
java.util.LinkedList<E>
+LinkedList()
Creates a default empty linked list.
+LinkedList(c: Collection<? extends E>) Creates a linked list from an existing collection.
+addFirst(o: E): void
Adds the object to the head of this list.
+addLast(o: E): void
Adds the object to the tail of this list.
+getFirst(): E
Returns the first element from this list.
+getLast(): E
Returns the last element from this list.
+removeFirst(): E
Returns and removes the first element from this list.
+removeLast(): E
Returns and removes the last element from this list.
20
Sections 22.7 – Static Methods for Lists & Collections
1. The Collections class contains various static methods for operating on collections and maps, for
creating synchronized collection classes, and for creating read-only collection classes.
java.util.Collections
List
+sort(list: List): void
Sorts the specified list.
+sort(list: List, c: Comparator): void
Sorts the specified list with the comparator.
+binarySearch(list: List, key: Object): int
Searches the key in the sorted list using binary search.
+binarySearch(list: List, key: Object, c:
Comparator): int
Searches the key in the sorted list using binary search
with the comparator.
+reverse(list: List): void
Reverses the specified list.
+reverseOrder(): Comparator
Returns a comparator with the reverse ordering.
+shuffle(list: List): void
Shuffles the specified list randomly.
+shuffle(list: List): void
Shuffles the specified list with a random object.
+copy(des: List, src: List): void
Copies from the source list to the destination list.
+nCopies(n: int, o: Object): List
Returns a list consisting of n copies of the object.
+fill(list: List, o: Object): void
Fills the list with the object.
+max(c: Collection): Object
Returns the max object in the collection.
+max(c: Collection, c: Comparator): Object Returns the max object using the comparator.
+min(c: Collection): Object
Collection
Returns the min object in the collection.
+min(c: Collection, c: Comparator): Object Returns the min object using the comparator.
+disjoint(c1: Collection, c2: Collection):
boolean
Returns true if c1 and c2 have no elements in common.
+frequency(c: Collection, o: Object): int
Returns the number of occurrences of the specified
element in the collection.
Sections 22.8 – Performance of Sets & Lists
1. The author did a comparison of sets and lists doing a number of random adds (Integers) and removes.
I’ve modified his example to differentiate between adds and removes. In each case, we generate
500,000 unique integers in random order and then add them. In the case of remove, we add them as
before and then randomly remove them. For the starred items (*), only 250,000 removes were
performed. All time is in seconds.
HashSet
LinkedHashSet
TreeSet
ArrayList
LinkedList
add() remove() Remove last item
0.17
0.17
0.11
0.15
0.36
0.48
0.03
47.10* 0.03
0.04
202.63* 0.03
21
Sections 22.9 – The Vector & Stack Classes
1. The Java Collections Framework was introduced with Java 2. Several data structures were supported
prior to Java 2. Among them are the Vector class and the Stack class. These classes were redesigned to
fit into the Java Collections Framework, but their old-style methods are retained for compatibility. This
section introduces the Vector class and the Stack class.
2. In Java 2, Vector is the same as ArrayList, except that Vector contains the synchronized methods for
accessing and modifying the vector. None of the new collection data structures introduced so far are
synchronized. If synchronization is required, you can use the synchronized versions of the collection
classes. These classes are introduced later in the section, “The Collections Class.”
3. The Vector class
«interface»
java.util.List<E>
java.util.Vector<E>
+Vector()
Creates a default empty vector with initial capacity 10.
+Vector(c: Collection<? extends E>)
Creates a vector from an existing collection.
+Vector(initialCapacity: int)
Creates a vector with the specified initial capacity.
+Vector(initCapacity:int, capacityIncr: int)
Creates a vector with the specified initial capacity and increment.
+addElement(o: E): void
Appends the element to the end of this vector.
+capacity(): int
Returns the current capacity of this vector.
+copyInto(anArray: Object[]): void
Copies the elements in this vector to the array.
+elementAt(index: int): E
Returns the object at the specified index.
+elements(): Enumeration<E>
Returns an enumeration of this vector.
+ensureCapacity(): void
Increases the capacity of this vector.
+firstElement(): E
Returns the first element in this vector.
+insertElementAt(o: E, index: int): void
Inserts o to this vector at the specified index.
+lastElement(): E
Returns the last element in this vector.
+removeAllElements(): void
Removes all the elements in this vector.
+removeElement(o: Object): boolean
Removes the first matching element in this vector.
+removeElementAt(index: int): void
Removes the element at the specified index.
+setElementAt(o: E, index: int): void
Sets a new element at the specified index.
+setSize(newSize: int): void
Sets a new size in this vector.
+trimToSize(): void
Trims the capacity of this vector to its size.
22
4. The Stack class
java.util.Vector<E>
java.util.Stack<E>
+Stack()
Creates an empty stack.
+empty(): boolean
Returns true if this stack is empty.
+peek(): E
Returns the top element in this stack.
+pop(): E
Returns and removes the top element in this stack.
+push(o: E) : E
Adds a new element to the top of this stack.
+search(o: Object) : int
Returns the position of the specified element in this stack.
Sections 22.10.2 – Queues & Priority Queues
1. A queue is a first-in/first-out data structure. Elements are appended to the end of the queue and are
removed from the beginning of the queue. In a priority queue, elements are assigned priorities. When
accessing elements, the element with the highest priority is removed first.
2. The Queue class
«interface»
java.util.Collection<E>
«interface»
java.util.Queue<E>
+offer(element: E): boolean
Inserts an element to the queue.
+poll(): E
Retrieves and removes the head of this queue, or null
if this queue is empty.
+remove(): E
Retrieves and removes the head of this queue and
throws an exception if this queue is empty.
+peek(): E
Retrieves, but does not remove, the head of this queue,
returning null if this queue is empty.
+element(): E
Retrieves, but does not remove, the head of this queue,
throwing an exception if this queue is empty.
23
3. The PriorityQueue class
«interface»
java.util.Queue<E>
java.util.PriorityQueue<E>
+PriorityQueue()
Creates a default priority queue with initial capacity 11.
+PriorityQueue(initialCapacity: int)
Creates a default priority queue with the specified initial
capacity.
+PriorityQueue(c: Collection<? extends
E>)
Creates a priority queue with the specified collection.
+PriorityQueue(initialCapacity: int,
comparator: Comparator<? super E>)
Creates a priority queue with the specified initial
capacity and the comparator.
4. Example – Priority Queue. We create a priority queue of Employee objects and use a Comparator that
sorts on salary, ascending. In other words, the lower the salary, the higher the priority. First, the
Comparator:
class EmployeeSalaryComparator implements Comparator<Employee>
{
public int compare( Employee e1, Employee e2 )
{
double salDif = e1.getSalary() - e2.getSalary();
if( salDif < 0 ) return -1;
else if( salDif > 0 ) return 1;
else return 0;
}
}
24
5. Example (continued).
Code
// Create a PriorityQueue with a comparator.
EmployeeSalaryComparator comp = new EmployeeSalaryComparator();
PriorityQueue<Employee> qEmps = new PriorityQueue<Employee>(10,comp);
// Create some employees.
Employee e1 = new Employee("Lyton", "Xavier",
Employee e2 = new Employee("Dern", "Donald",
Employee e3 = new Employee("Lyton", "Nelly",
Employee e4 = new Employee("Reilly", "Junita",
Employee e5 = new Employee("Colter", "Ben",
Employee e6 = new Employee("Mixton", "Zerby",
Employee e7 = new Employee("Reilly", "Junita",
243558673,
476227851,
338290448,
338290448,
883456321,
422789012,
126270942,
77.88);
23.44);
82.45);
82.45);
12.89);
22.76);
23.44);
// Put employees in queue
qEmps.offer(e1);
qEmps.offer(e2);
qEmps.offer(e3);
qEmps.offer(e4);
qEmps.offer(e5);
qEmps.offer(e6);
qEmps.offer(e7);
6. Example (continued). Use the iterator (or enhanced for loop) to access elements. Notice, there is no
order! Or is there? You’ll find out in CS 3410.
Code
Iterator<Employee> iter = qEmps.iterator();
while( iter.hasNext() )
System.out.println( iter.next() );
Output
(Colter, Ben - 883456321, 12.89)
(Dern, Donald - 476227851, 23.44)
(Mixton, Zerby - 422789012, 22.76)
(Reilly, Junita - 338290448, 82.45)
(Lyton, Xavier - 243558673, 77.88)
(Lyton, Nelly - 338290448, 82.45)
(Reilly, Junita - 126270942, 23.44)
7. Example (continued). Remove all the elements from the queue and print. Removes in the correct order
(by salary) and appears to preserve the order for ties. In other words, if two Employees have the same
salary, then the one that arrived first, leaves first. You would like a queue to have such a priority, but
the text says ties are broken arbitrarily.
Code
Output
(Colter, Ben - 883456321, 12.89)
(Mixton, Zerby - 422789012, 22.76)
(Dern, Donald - 476227851, 23.44)
(Reilly, Junita - 126270942, 23.44)
(Lyton, Xavier - 243558673, 77.88)
(Lyton, Nelly - 338290448, 82.45)
(Reilly, Junita - 338290448, 82.45)
while (qEmps.size() > 0)
System.out.println(qEmps.remove());
25
8. Note, you cannot change the priority of an item once it is in the PriorityQueue. If you do, you will
destroy the ordering. The Comparator is only used when inserting into the PriorityQueue. If you did
need to change a priority, you would need to remove the item, change the priority, and then re-insert
it.
Sections 22.10.1 – The LinkedList Class & Deque Interface
1. The Deque interface is a sub-interface of Queue. It defines methods to: addFirst, removeFirst, getFirst,
addLast, removeLast, getLast. “Deque” is short for “double-ended queue” and is pronounced, “deck”.
2. The LinkedList class implements the List interface and the Deque interface. Thus, a LinkedList can be
used to model a queue.
3. Example – modeling a first-in, first-out (FIFO) queue.
Code
// Create Queue from LinkedList
LinkedList<Employee> qEmps = new
LinkedList<Employee>();
Output
// Create some new employees.
Employee e1 = new Employee("Lyton", "Xavier",
243558673, 77.88);
Employee e2 = new Employee("Dern", "Donald",
476227851, 23.44);
Employee e3 = new Employee("Lyton", "Nelly",
338290448, 82.45);
// Put employees in Queue
qEmps.offer(e1);
qEmps.offer(e2);
qEmps.offer(e3);
// Use Iterator to visit employees, order is
FIFO.
Iterator<Employee> iter = qEmps.iterator();
while( iter.hasNext() )
System.out.println( iter.next() );
(Lyton, Xavier - 243558673, 77.88)
(Dern, Donald - 476227851, 23.44)
(Lyton, Nelly - 338290448, 82.45)
// Remove elements from the queue, Order is
// FIFO
while (qEmps.size() > 0)
System.out.println(qEmps.remove());
26
(Lyton, Xavier - 243558673, 77.88)
(Dern, Donald - 476227851, 23.44)
(Lyton, Nelly - 338290448, 82.45)
Sections 22.11 – Maps
1. The Map interface maps keys to the elements. The keys are like indexes. In List, the indexes are
integer. In Map, the keys can be any objects.
Search keys
Corresponding values
Entry
…
.
.
2. The Map Interface:
java.util.Map<K, V>
+clear(): void
Removes all mappings from this map.
+containsKey(key: Object): boolean
Returns true if this map contains a mapping for the
specified key.
+containsValue(value: Object): boolean Returns true if this map maps one or more keys to the
specified value.
+entrySet(): Set
Returns a set consisting of the entries in this map.
+get(key: Object): V
Returns the value for the specified key in this map.
+isEmpty(): boolean
Returns true if this map contains no mappings.
+keySet(): Set<K>
Returns a set consisting of the keys in this map.
+put(key: K, value: V): V
Puts a mapping in this map.
+putAll(m: Map): void
Adds all the mappings from m to this map.
+remove(key: Object): V
Removes the mapping for the specified key.
+size(): int
Returns the number of mappings in this map.
+values(): Collection<V>
Returns a collection consisting of the values in this map.
27
3. Concrete Map Classes
«interface»
java.util.Map<K, V>
«interface»
java.util.AbstractMap<K, V>
java.util.SortedMap<K, V>
+firstKey(): K
java.util.HashMap<K, V>
+lastKey(): K
+comparator(): Comparator<? super K>)
+HashMap()
+headMap(toKey: K): SortedMap
+HashMap(m: Map)
+tailMap(fromKey: K): SortedMap
java.util.LinkedHashMap<K, V>
java.util.TreeMap<K, V>
+LinkedHashMap()
+LinkedHashMap(m: Map)
+TreeMap()
+LinkedHashMap(initialCapacity: int,
loadFactor: float, accessOrder: boolean)
+TreeMap(m: Map)
+TreeMap(c: Comparator<? super K>)
4. HashMap and TreeMap – The HashMap and TreeMap classes are two concrete implementations of the
Map interface. The HashMap class is efficient for locating a value, inserting a mapping, and deleting a
mapping. The TreeMap class, implementing SortedMap, is efficient for traversing the keys in a sorted
order.
5. LinkedHashMap – LinkedHashMap was introduced in JDK 1.4. It extends HashMap with a linked list
implementation that supports an ordering of the entries in the map. The entries in a HashMap are not
ordered, but the entries in a LinkedHashMap can be retrieved in the order in which they were inserted
into the map (known as the insertion order), or the order in which they were last accessed, from least
recently accessed to most recently (access order). The no-arg constructor constructs a LinkedHashMap
with the insertion order. To construct a LinkedHashMap with the access order, use the
LinkedHashMap(initialCapacity, loadFactor, true).
28
HashMap Code Example
1. The put method and looping through the values.
Code
// Create an empty hashmap.
Map<Integer,Employee> hmEmployees = new
HashMap<Integer,Employee>();
Output
// Create some new employees.
Employee e1 = new Employee("Lyton", "Xavier",
243558673, 77.88);
Employee e2 = new Employee("Dern", "Donald",
476227851, 23.44);
// Put students in hashmap, use SSN as key.
// A successful put returns a "null"
System.out.println( hmEmployees.put(
e1.getSSNum(), e1) );
// If the key exists, then a replace occurs
// and the old value is returned
System.out.println( hmEmployees.put(
e1.getSSNum(), e2) );
// Display values
for( Employee e : hmEmployees.values() )
System.out.println( e );
null
Lyton, Xavier - 243558673, 77.88
Dern, Donald - 476227851, 23.44
2. The get method and looping through the values.
Code
// Display values in hashmap
for( Employee e : hmEmployees.values() )
System.out.println( e );
// Get a value by key
// Successful
System.out.println(
hmEmployees.get( 243558673 ) );
// Unsuccessful
System.out.println(
hmEmployees.get( 123456789 ) );
Output
Dern, Donald - 476227851, 23.44
Lyton, Xavier - 243558673, 77.88
Lyton, Nelly - 338290448, 82.45
Lyton, Xavier - 243558673, 77.88
null
29
3. Removing values.
Code
// Display values
for( Employee e : hmEmployees.values() )
System.out.println( e );
// Successful
System.out.println( hmEmployees.remove(
243558673 ) );
// Not successful
System.out.println( hmEmployees.remove(
123456789 ) );
// Display values
for( Employee e : hmEmployees.values() )
System.out.println( e );
30
Output
Dern, Donald - 476227851, 23.44
Lyton, Xavier - 243558673, 77.88
Lyton, Nelly - 338290448, 82.45
Lyton, Xavier - 243558673, 77.88
null
Dern, Donald - 476227851, 23.44
Lyton, Nelly - 338290448, 82.45
4. The putAll method.
Code
// Create an empty hashmap.
Map<Integer,Employee> hmEmployees = new
HashMap<Integer,Employee>();
Output
// Create some new employees.
Employee e1 = new Employee("Lyton", "Xavier",
243558673, 77.88);
Employee e2 = new Employee("Dern", "Donald",
476227851, 23.44);
Employee e3 = new Employee("Lyton", "Nelly",
338290448, 82.45);
Employee e4 = new Employee("Reilly", "Junita",
178890463, 82.45);
Employee e5 = new Employee("Colter", "Ben",
883456321, 12.89);
Employee e6 = new Employee("Mixton", "Zerby",
422789012, 22.76);
// Clear hashmap and add some employees.
hmEmployees.clear();
hmEmployees.put(e1.getSSNum(), e1);
hmEmployees.put(e2.getSSNum(), e2);
hmEmployees.put(e3.getSSNum(), e3);
// Display hashmap 1 values.
for( Employee e : hmEmployees.values() )
System.out.println( e );
Dern, Donald - 476227851, 23.44
Lyton, Xavier - 243558673, 77.88
Lyton, Nelly - 338290448, 82.45
// Create a 2nd hashmap and add some emps.
Map<Integer,Employee> hmEmployees2 = new
HashMap<Integer,Employee>();
hmEmployees2.put(e4.getSSNum(), e4);
hmEmployees2.put(e5.getSSNum(), e5);
hmEmployees2.put(e6.getSSNum(), e6);
// Display hashmap 2 values.
for( Employee e : hmEmployees2.values() )
System.out.println( e );
Colter, Ben - 883456321, 12.89
Reilly, Junita - 178890463, 82.45
Mixton, Zerby - 422789012, 22.76
// Put 2nd map into first.
hmEmployees.putAll( hmEmployees2 );
// Display hashmap 1 values.
for( Employee e : hmEmployees.values() )
System.out.println( e );
31
Dern, Donald - 476227851, 23.44
Colter, Ben - 883456321, 12.89
Lyton, Xavier - 243558673, 77.88
Reilly, Junita - 178890463, 82.45
Lyton, Nelly - 338290448, 82.45
Mixton, Zerby - 422789012, 22.76
5. The containsKey and containsValue methods. Must override equals and hashCode. Overriding
hashCode is beyond the scope of this class. We show both methods below.
Code
// See if a key is present.
System.out.println( hmEmployees.containsKey( e5.getSSNum()) );
// Create a dummy employee. Use to search for by value.
// Note how equals is defined in Employee class.
Employee eDummy = new Employee("Colter", "Ben", 0,0 );
// See if a value is present.
System.out.println( hmEmployees.containsValue( eDummy ) );
Output
true
true
public boolean equals(Object o)
{
Employee e = (Employee)o;
int lName = this.getLastName().compareTo(e.getLastName());
if( lName == 0 )
{
int fName = this.getFirstName().compareTo(e.getFirstName());
if( fName == 0 )
return true;
}
return false;
}
public int hashCode()
{
int x = lName.hashCode();
int y = fName.hashCode();
int z = 23*x + 7*y;
return z;
}
32
6. The entrySet method, MapEntry class
Code
// Display hashmap values
for( Employee e : hmEmployees.values() )
System.out.println( e );
Output
Dern, Donald - 476227851, 23.44
Lyton, Xavier - 243558673, 77.88
Lyton, Nelly - 338290448, 82.45
// Create EntrySet
Set<Map.Entry<Integer,Employee>> eSet =
hmEmployees.entrySet();
// Create Iterator for EntrySet.
Iterator<Map.Entry<Integer,Employee>> iter =
eSet.iterator();
// Loop over EntrySet.
while( iter.hasNext() )
{
// Grab the map entry.
Map.Entry<Integer,Employee> mapEntry =
iter.next();
// Extract key and value.
int key = mapEntry.getKey();
Employee value = mapEntry.getValue();
// Possibly change value.
if( key == 338290448 )
{
Employee eChanged =
new Employee("Fudd", "Elmer",
338290448, 7.14);
mapEntry.setValue( eChanged );
}
}
// Display hashmap values
for( Employee e : hmEmployees.values() )
System.out.println( e );
33
Dern, Donald - 476227851, 23.44
Lyton, Xavier - 243558673, 77.88
Fudd, Elmer - 338290448, 7.14
7. The keySet method, ordering the keys, using keys to access Employees
Code
// Display hashmap values
for( Employee e : hmEmployees.values() )
System.out.println( e );
Output
Dern, Donald - 476227851, 23.44
Lyton, Xavier - 243558673, 77.88
Fudd, Elmer - 338290448, 7.14
// Get set of keys.
Set<Integer> keys = hmEmployees.keySet();
// Display unordered keys
for( int key : keys )
System.out.println( key );
476227851
243558673
338290448
// Order the keys.
TreeSet<Integer> orderedKeys = new
TreeSet<Integer>( keys );
// Display employees ordered by key.
for( int key : orderedKeys )
System.out.println( hmEmployees.get(key) );
Lyton, Xavier - 243558673, 77.88
Fudd, Elmer - 338290448, 7.14
Dern, Donald - 476227851, 23.44
8. Manipulating the values
Code
// Display hashmap values
for( Employee e : hmEmployees.values() )
System.out.println( e );
Output
Dern, Donald - 476227851, 23.44
Lyton, Xavier - 243558673, 77.88
Fudd, Elmer - 338290448, 7.14
// Get values collection.
Collection<Employee> cEmployees =
hmEmployees.values();
// Turn collection into list
List<Employee> lEmployees = new
ArrayList<Employee>( cEmployees );
// Sort the list
Collections.sort( lEmployees, new
EmployeeNameComparator() );
// Display hashmap values
for( Employee e : lEmployees )
System.out.println( e );
Dern, Donald - 476227851, 23.44
Fudd, Elmer - 338290448, 7.14
Lyton, Xavier - 243558673, 77.88
34
TreeMap Code Example
Code
// Create an empty treemap.
SortedMap<Integer,Employee> tmEmployees = new
TreeMap<Integer,Employee>();
Output
// Create some new employees.
Employee e1 = new Employee("Lyton", "Xavier",
243558673, 77.88);
Employee e2 = new Employee("Dern", "Donald",
476227851, 23.44);
Employee e3 = new Employee("Lyton", "Nelly",
945678345, 41.45);
Employee e4 = new Employee("Reilly", "Junita",
338290448, 82.45);
Employee e5 = new Employee("Colter", "Ben",
883456321, 12.89);
Employee e6 = new Employee("Mixton", "Zerby",
422789012, 22.76);
// Put students in treemap.
tmEmployees.put(e1.getSSNum(),
tmEmployees.put(e2.getSSNum(),
tmEmployees.put(e3.getSSNum(),
tmEmployees.put(e4.getSSNum(),
tmEmployees.put(e5.getSSNum(),
tmEmployees.put(e6.getSSNum(),
e1);
e2);
e3);
e4);
e5);
e6);
// headMap example.
SortedMap<Integer,Employee> smEmployees =
tmEmployees.headMap( 555555555 );
// Display, headMap.
for( Employee e : smEmployees.values() )
System.out.println( e );
Lyton, Xavier - 243558673, 77.88
Reilly, Junita - 338290448, 82.45
Zenton, Violet - 422789012, 22.76
Dern, Donald - 476227851, 23.44
// tailMap example.
smEmployees =
tmEmployees.tailMap( 555555555 );
// Display, tailMap.
for( Employee e : smEmployees.values() )
System.out.println( e );
Colter, Ben - 883456321, 12.89
Lyton, Nelly - 945678345, 41.45
// subMap example.
smEmployees =
tmEmployees.subMap( 300000000, 900000000 );
// Display, subMap.
for( Employee e : smEmployees.values() )
System.out.println( e );
35
Reilly, Junita - 338290448, 82.45
Zenton, Violet - 422789012, 22.76
Dern, Donald - 476227851, 23.44
Colter, Ben - 883456321, 12.89
Sections 22.12 – Singleton & Unmodifiable Collections & Maps
1. Omit
36
Download