0% found this document useful (0 votes)
106 views

My Collection1

This document discusses backed collections in Java and how they work. 1. A backed collection is a partial view or subset of another collection that is dynamically updated when the original collection is modified. 2. When a backed collection like a TreeMap submap is created, it defines both the included range and the valid range for additions to the copy. 3. Methods like subMap(), headMap(), and tailMap() are used to create backed collections from Maps and Sets. Unless specified, the starting point is inclusive and the endpoint exclusive.
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
106 views

My Collection1

This document discusses backed collections in Java and how they work. 1. A backed collection is a partial view or subset of another collection that is dynamically updated when the original collection is modified. 2. When a backed collection like a TreeMap submap is created, it defines both the included range and the valid range for additions to the copy. 3. Methods like subMap(), headMap(), and tailMap() are used to create backed collections from Maps and Sets. Unless specified, the starting point is inclusive and the endpoint exclusive.
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 27

Backed Collections

TreeMap<String, String> map = new TreeMap<String, String>(); map.put("a", "ant"); map.put("d", "dog"); map.put("h", "horse"); SortedMap<String, String> submap; submap = map.subMap("b", "g"); // #1 create a backed collection System.out.println(map + " " + submap); // #2 show contents map.put("b", "bat"); // #3 add to original submap.put("f", "fish"); // #4 add to copy map.put("r", "raccoon"); // #5 add to original - out of range // submap.put("p", "pig"); // #6 add to copy - out of range System.out.println(map + " " + submap); // #7 show final contents This should produce something like this: {a=ant, d=dog, h=horse} {d=dog} {a=ant, b=bat, d=dog, f=fish, h=horse, r=raccoon} {b=bat, d=dog, f=fish} When submap was created, we provided a value range for the new collection. This range defines not only what should be included when the partial copy is created, but also defines the range of values that can be added to the copy.

Contd

we can add a new entry to the original collection, even if it's outside the range of the copy. ---Some methods to create a backed collection --- TreeSet(headSet(), subSet(), and tailSet()) TreeMap (headMap(), subMap(), and tailMap()). Unless specifically indicated by a boolean argument, a subset's starting point will always be inclusive. If they exist its a Java 6 method that lets you specify whether the endpoint is exclusive, and these methods return a NavigableXxx. If the boolean argument(s) dont exist, the method returns either a SortedSet or a SortedMap.

Method
headSet(e, b) headMap(k, b) tailSet(e, b) tailMap(k, b)
subSet(s, b, e, b)

Description
Returns a subset ending at element e and exclusive of e Returns a submap ending at key k and exclusive of key k Returns a subset starting at and inclusive of element e Returns a submap starting at and inclusive of key k Returns a subset starting at element s and ending just before element e

subMap(s, b, e, Returns a submap starting at key s and ending just before b) key s

Rule for exam

Invoking pollFirstXxx() on the copy will remove an entry from both collections, but invoking pollFirstXxx() on the original will remove only the entry from the original because original and copy collections have different first elements due to tailXxx() or subXxx() method. PriorityQueue Class
A PriorityQueue orders its elements using a user-defined priority otherwise uses natural ordering.

Prog.
import java.util.*; class PQ { static class PQsort implements Comparator<Integer> { // inverse sort public int compare(Integer one, Integer two) { return two - one; // unboxing } } public static void main(String[] args) { int[] ia = {1,5,3,7,6,9,8 }; // unordered data PriorityQueue<Integer> pq1 = new PriorityQueue<Integer>(); // use natural order for(int x : ia) // load queue

Prog contd pq1.offer(x); for(int x : ia) // review queue System.out.print(pq1.poll() + " "); System.out.println(""); PQsort pqs = new PQsort(); // get a Comparator PriorityQueue<Integer> pq2 = new PriorityQueue<Integer>(10,pqs); // use Comparator for(int x : ia) // load queue pq2.offer(x); System.out.println("size " + pq2.size()); System.out.println("peek " + pq2.peek()); System.out.println("size " + pq2.size()); System.out.println("poll " + pq2.poll()); System.out.println("size " + pq2.size()); for(int x : ia) // review queue System.out.print(pq2.poll() + " "); } } This code produces something like this: 1356789 size 7 peek 9 size 7 poll 9 size 6 8 7 6 5 3 1 null

Key methods in java.util.Arrays

static List asList(T[]) static int binarySearch(Object[], key), static int binarySearch(prim[], key) static int binarySearch(T[], key, Comparator) static boolean equals(Object[], Object[]), static boolean equals(prim[], prim[]) public static void sort(Object[ ] ), public static void sort(prim[ ] ) public static String toString(Object[]) Key methods in java.util.Collections static int binarySearch(List, key, Comparator) static void reverse(List) static Comparator reverseOrder(Comparator) static void sort(List, Comparator)
String[] sa = {">ff<", "> f<", ">f <", ">FF<" }; // ordered? PriorityQueue<String> pq3 = new PriorityQueue<String>(); for(String s : sa) pq3.offer(s); for(String s : sa) System.out.print(pq3.poll() + " ");---o/p------- > f< >FF< >f < >ff< We have to remember that spaces sort before characters and that uppercase letters sort before lowercase characters.

Arrays in Java have always been type safean array declared as type String (String[]) can't accept Integers (or ints), Dogs, or anything other than Strings. As of Java 5,we can use generics for making type safe collections. Pre java 5--- List myList = new ArrayList();---can hold anything When we get object from collection it is of return type java.lang.Object,so for getting a string back from a string intended list casting is required like String s = (String) myList.get(0); also it can fail at runtime. So, generics takes care of both ends (the putting in and getting out) by enforcing the type of your collections. List<String> myList=newArrayList<String>(); myList.add("Fred"); // OK, it will hold Strings myList.add(new Dog()); // compiler error!! The compiler already knows that myList contains only things that can be assigned to a String reference, so now there's no need for a cast.so String s = myList.get(0); Before generics code can be List<Object> myList = new ArrayList<Object>(); You can declare a type parameter for a method argument, which then makes the argument a type safe reference: void takeListOfStrings(List<String> strings) { strings.add("foo");} // no problem adding a String The method above would NOT compile if we changed it to void takeListOfStrings(List<String> strings) { strings.add(new Integer(42)); // NO!! strings is type safe}

Generics

Contd.

Return types can obviously be declared type safe as well: public List<Dog> getDogList() { List<Dog> dogs = new ArrayList<Dog>();//code to add dog return dogs;} Dog d = getDogList().get(0);----java 5 no casting as dog is coming out Dog d = (Dog)getDogList().get(0);----pre java 5 need to cast

Converting legacy code to use generics


You just add a type in angle brackets (<>)immediately following the collection type in BOTH the variable declaration and the constructor call, including any place you declare a variable (so that means arguments and return types too). public List changeStrings(ArrayList s) { } can be converted to public List<String> changeStrings(ArrayList<String> s) { }

Mixing generic and non-generic collections

import java.util.*; public class TestLegacy { public static void main(String[] args) { List<Integer> myList = new ArrayList<Integer>(); // type safe collection myList.add(4); myList.add(6); Adder adder = new Adder(); int total = adder.addAll(myList); System.out.println(total); } }

------

Legacy class used-------

import java.util.*; class Adder { int addAll(List list) { // method with a non-generic List argument, Iterator it = list.iterator(); int total = 0; while (it.hasNext()) { int i = ((Integer)it.next()).intValue(); total += i; } return total; } }

Conclusion- In that example, there was no risk to the caller's code, but the legacy method might have blown up if the list passed in contained anything but Integers (which would cause a ClassCastException).

import java.util.*; public class TestBadLegacy { public static void main(String[] args) { List<Integer> myList = new ArrayList<Integer>(); myList.add(4); myList.add(6); Inserter in = new Inserter(); in.insert(myList); // pass List<Integer> to legacy code } } class Inserter { // method with a non-generic List argument void insert(List list) { list.add(new Integer(42)); // adds to the incoming list } }

void insert(List list) { list.add(newString("42")); // put a String in the list passed in It both compiles and runs as the older legacy code was allowed to put anything (except primitives) into a collection. In fact the compiler will warn you that you're taking a big, big risk sending your nice protected ArrayList<Integer> into a dangerous method that can have its way with your list and put in Floats, Strings, or even Dogs.

Note-Remember that compiler warnings are NOT considered a compiler failure

The JVM has no idea that your ArrayList was supposed to hold only Integers. The typing information does not exist at runtime! All your generic code is strictly for the compiler. Through a process called "type erasure," the compiler does all of its verifications on your generic code and then strips the type information out of the class byte code.

Arrays vs collection
Arrays give you BOTH compile-time protection and runtime protection but collections give you compile time protection as that guarantees you won't put the wrong thing into a typed collection, and it also eliminates the need for a cast when you get something out,since the compiler already knows that only an Integer is coming out of an Integer list.

So we have to pay attention to the compile time warnings like javac TestBadLegacy.java Note: TestBadLegacy.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. The act of adding a String to an <Integer> list won't fail at runtime until you try to treat that String-you-think-is-an-Integer as an Integer. When you are pulling out something from type safe ArrayList<Integer>, the code that actually adds the String to the list runs. If you try to retrieve the string and try to assign it to an Integer reference or invoke an Integer method, you're dead. The moment you turn that type safe collection over to older, non-type safe code, your protection vanishes.

Unboxing problem List test = new ArrayList(); test.add(43); int x = (Integer)test.get(0);

//casting necessary in non-type safe collection

Polymorphism and generics


List<Integer> myList = new ArrayList<Integer>(); we were able to assign an ArrayList to a List reference, because List is a supertype of ArrayList. List<Parent> myList = new ArrayList<Child>();---- it wont work. The rule is herethe type of the variable declaration must match the type you pass to the actual object type not its sub type or its super type. Polymorphism applies here to only the "base" type. And by "base," we mean the type of the collection class Itself. List and ArrayList are the base types. In arrays we can do like Parent[] myArray = new Child[3]; but polymorphism does not work the same way for generics as it does with arrays.

Generic methods
public class AnimalDoctor { // method takes an array of any animal subtype public void checkAnimals(Animal[] animals) { for(Animal a : animals) { a.checkup(); } } public static void main(String[] args) { // test it Dog[] dogs = {new Dog(), new Dog()}; Cat[] cats = {new Cat(), new Cat(), new Cat()}; Bird[] birds = {new Bird()}; AnimalDoctor doc = new AnimalDoctor(); doc.checkAnimals(dogs); // pass the Dog[] doc.checkAnimals(cats); // pass the Cat[] doc.checkAnimals(birds); // pass the Bird[]
} }

In case of array

public class AnimalDoctorGeneric { public void checkAnimals(ArrayList<Animal> animals) { for(Animal a : animals) { a.checkup(); } } public static void main(String[] args) { // make ArrayLists instead of arrays for Dog, Cat, Bird List<Dog> dogs = new ArrayList<Dog>(); dogs.add(new Dog()); dogs.add(new Dog()); List<Cat> cats = new ArrayList<Cat>(); cats.add(new Cat()); cats.add(new Cat()); List<Bird> birds = new ArrayList<Bird>(); birds.add(new Bird()); // this code is the same as the Array version AnimalDoctorGeneric doc = new AnimalDoctorGeneric(); // this worked when we used arrays instead of ArrayLists doc.checkAnimals(dogs); // send a List<Dog> doc.checkAnimals(cats); // send a List<Cat> doc.checkAnimals(birds); // send a List<Bird> In case of generics

Contd..
The compiler stops us with errors, not warnings. You simply CANNOT assign the individual ArrayLists of Animal subtypes (<Dog>, <Cat>, or <Bird>) to an ArrayList of the super type <Animal>, which is the declared type of the argument. Animal[] animals = new Animal[3]; animals[0] = new Cat(); animals[1] = new Dog(); we're using polymorphism not for the object that the array reference points to, but rather what the array can actually HOLDin this case, any subtype of Animal. You can do the same thing with generics like List<Animal> animals = new ArrayList<Animal>(); animals.add(new Cat()); // OK animals.add(new Dog()); // OK Why can't you pass an ArrayList<Dog> to an argument of ArrayList<Animal>? ------it is dangerous to pass a collection (array or ArrayList) of a subtype into a method that takes a collection of a super type. The reason the compiler won't let you pass an ArrayList<Dog> into a method that takes an ArrayList<Animal>, is because within the method, that parameter is of type ArrayList<Animal>, and that means you could put any kind of Animal into it. There would be no way for the compiler to stop you from putting a Dog into a List that was originally declared as <Cat>, but is now referenced from the <Animal> parameter.

Why good for array


The reason you can get away with compiling this for arrays is because there is a runtime exception (ArrayStoreException) that will prevent you from putting the wrong type of object into an array. If you send a Dog array into the method that takes an Animal array, and you add only Dogs (including Dog subtypes, of course) into the array now referenced by Animal, no problem. But if you DO try to add a Cat to the object that is actually a Dog array, you'll get the exception. All the generic type information is removed during compilation, so by the time it gets to the JVM, there is simply no way to recognize the disaster of putting a Cat into an ArrayList<Dog> and vice versa. If you try to invoke addAnimal() with an argument of any OTHER ArrayList type, the compiler will stop you, since at runtime the JVM would have no way to stop you from adding a Dog to what was created as a Cat collection. If you change the generic type to dog without changing the addAnimal() it wont compile. There IS a mechanism to tell the compiler that you can take any generic subtype of the declared argument type because you won't be putting anything in the collection. And that mechanism is the wildcard <?>. public void addAnimal(List<? extends Animal> animals) I can be assigned a collection that is a subtype of List and typed for <Animal> or anything that extends Animal and I will not ADD anything into the collection.

Contd..
public void addAnimal(List<? extends Animal> animals) { animals.add(new Dog());}---we cant add here and if we are not changing our code not to add anything then fine. The keyword extends in the context of a wildcard represents BOTH subclasses and interface implementations. There is no <? implements Serializable> syntax. Another super keyword is there like public void addAnimal(List<? super Dog> animals). So it can accept any List with a generic type that is of type Dog, or a supertype of Dog. Nothing lower in the inheritance tree can come in, but anything higher than Dog is OK. A collection declared as any super type of Dog will be able to accept a Dog as an element. List<Animal> can take a Dog. public void foo(List<?> list) { }----any type of list assigned to argument public void foo(List<Object> list) { }---can pass only List<Object> The wildcards can be used only for reference declarations (including arguments, variables, return types, and so on). They can't be used as the type parameter when you create a new typed collection. Think about thatwhile a reference can be abstract and polymorphic, the actual object created must be of a specific type. You have to lock down the type when you make the object using new. List<? extends Dog> cList = new ArrayList<Integer>();---Integer list to a reference that takes only a Dog List<?> foo = new ArrayList<? extends Animal>();----illegal declaration List<? super Animal> dList = new ArrayList<Dog>();---dog too low in hierarchy can assign only Object or Animal

Generic declaration
API declaration for the java.util.List interface:public interface List<E>--- <E>placeholder for the type you pass in. Add() looks like boolean add(E o) . whatever E is when you declare the List, that's what you can add to it. You can use more than one parameterized type in a single class definition:

public class UseTwo<T, X> { T one; X two; UseTwo(T one, X two) { this.one = one; this.two = two; } T getT() { return one; } X getX() { return two; } // test it by creating it with <String, Integer> public static void main (String[] args) { UseTwo<String, Integer> twos =new UseTwo<String, Integer>("foo", 42); String theT = twos.getT(); // returns a String int theX = twos.getX(); // returns Integer, unboxes to int } }

Creating generic methods


It's possible to define a parameterized type at a more granular levela method. we can declare the method without a specific type and then get the type information based on the type of the object passed to the method. The class doesnt need to be generic. import java.util.*; public class CreateAnArrayList { public <T> void makeArrayList(T t) { // take an object of an unknown "T" type List<T> list = new ArrayList<T>(); // now we can create the list using "T" list.add(t); } } If you invoke the makeArrayList() method with a Dog instance, the method will behave lke public void makeArrayList(Dog t) { List<Dog> list = new ArrayList<Dog>(); list.add(t);} The <T> before void simply defines what T is before you use it as a type in the argument. If method looks like public void makeList(T t) { } ,then t is a type declaration for a variable of class T. So a class can be like class X { public <X> X(X x) { } } ---no naming conflict between class names, type parameter placeholders, and variable identifiers. Wildcard syntax does NOT work for generic class and method declarations.

Self test
1. public static void main(String[] args) { // INSERT DECLARATION HERE for (int i = 0; i <= 10; i++) { List<Integer> row = new ArrayList<Integer>(); for (int j = 0; j <= 10; j++) row.add(i * j); table.add(row); } for (List<Integer> row : table) System.out.println(row); } Which statements could be inserted at // INSERT DECLARATION HERE to allow this code to compile and run? (Choose all that apply.) A. List<List<Integer>> table = new List<List<Integer>>(); B. List<List<Integer>> table = new ArrayList<List<Integer>>(); C. List<List<Integer>> table = new ArrayList<ArrayList<Integer>>(); D. List<List, Integer> table = new List<List, Integer>(); E. List<List, Integer> table = new ArrayList<List, Integer>(); F. List<List, Integer> table = new ArrayList<ArrayList, Integer>(); G. None of the above

Answer:

B is correct. A is incorrect because List is an interface, so you can't say new List() regardless of any generic types. D, E, and F are incorrect because List only takes one type parameter (a Map would take two, not a List). C is tempting, but incorrect. The type argument <List<Integer>> must be the same for both sides of the assignment, even though the constructor new ArrayList() on the right side is a subtype of the declared type List on the left.

Given: 12. public class AccountManager { 13. private Map accountTotals = new HashMap(); 14. private int retirementFund; 15. 16. public int getBalance(String accountName) { 17. Integer total = (Integer) accountTotals.get(accountName); 18. if (total == null) 19. total = Integer.valueOf(0); 20. return total.intValue(); 21. } 23. public void setBalance(String accountName, int amount) { 24. accountTotals.put(accountName, Integer.valueOf(amount)); 25. } } This class is to be updated to make use of appropriate generic types, with no changes in behavior (for better or worse). Which of these steps could be performed? (Choose three.) A. Replace line 13 with private Map<String, int> accountTotals = new HashMap<String, int>(); B. Replace line 13 with private Map<String, Integer> accountTotals = new HashMap<String, Integer>(); C. Replace line 13 with private Map<String<Integer>> accountTotals = new HashMap<String<Integer>>(); D. Replace lines 1720 with int total = accountTotals.get(accountName); if (total == null) total = 0; return total; E. Replace lines 1720 with Integer total = accountTotals.get(accountName); if (total == null) total = 0; return total; F. Replace lines 1720 with return accountTotals.get(accountName); G. Replace line 24 with accountTotals.put(accountName, amount); H. Replace line 24 with accountTotals.put(accountName, amount.intValue());

Q2 .

Answer B, E, and G are correct. A is wrong because you can't use a primitive type as a type parameter. C is wrong because a Map takes two type parameters separated by a comma. D is wrong because an int can't autobox to a null, and F is wrong because a null can't unbox to 0. H is wrong because you can't autobox a primitive just by trying to invoke a method with it.

interface Hungry<E> { void munch(E x); } interface Carnivore<E extends Animal> extends Hungry<E> {} interface Herbivore<E extends Plant> extends Hungry<E> {} abstract class Plant {} class Grass extends Plant {} abstract class Animal {} class Sheep extends Animal implements Herbivore<Sheep> { public void munch(Sheep x) {} } class Wolf extends Animal implements Carnivore<Sheep> { public void munch(Sheep x) {} } Which of the following changes (taken separately) would allow this code to compile? (Choose all that apply.) A. Change the Carnivore interface to B. interface Carnivore<E extends Plant> extends Hungry<E> {} B. Change the Herbivore interface to interface Herbivore<E extends Animal> extends Hungry<E> {} C. Change the Sheep class to class Sheep extends Animal implements Herbivore<Plant> { public void munch(Grass x) {} } D. Change the Sheep class to class Sheep extends Plant implements Carnivore<Wolf> { public void munch(Wolf x) {} } E. Change the Wolf class to class Wolf extends Animal implements Herbivore<Grass> { public void munch(Grass x) {} } F. No changes are necessary

Q3.

Answer
B is correct. The problem with the original code is that Sheep tries to implement Herbivore<Sheep> and Herbivore declares that its type parameter E can be any type that extends Plant. Since a Sheep is not a Plant, Herbivore<Sheep> makes no sense the type Sheep is outside the allowed range of Herbivore's parameter E. Only solutions that either alter the definition of a Sheep or alter the definition of Herbivore will be able to fix this. So A, E, and F are eliminated. B works, changing the definition of an Herbivore to allow it to eat Sheep solves the problem. C doesn't work because an Herbivore<Plant> must have a munch(Plant) method, not munch(Grass). And D doesn't work, because in D we made Sheep extend Plant, now the Wolf class breaks because its munch(Sheep) method no longer fulfills the contract of Carnivore.

Q4.
PriorityQueue<String> pq = new PriorityQueue<String>(); pq.add("2"); pq.add("4"); System.out.print(pq.peek() + " "); pq.offer("1"); pq.add("3"); pq.remove("1"); System.out.print(pq.poll() + " "); if(pq.remove("2")) System.out.print(pq.poll() + " "); System.out.println(pq.poll() + " " + pq.peek()); What is the result? A. 2 2 3 3 B. 2 2 3 4 C. 4 3 3 4 D. 2 2 3 3 3 E. 4 3 3 3 3 F. 2 2 3 3 4 G. Compilation fails H. An exception is thrown at runtime

Q5.
TreeSet<String> s = new TreeSet<String>(); TreeSet<String> subs = new TreeSet<String>(); s.add("a"); s.add("b"); s.add("c"); s.add("d"); s.add("e"); subs = (TreeSet)s.subSet("b", true, "d", true); s.add("g"); s.pollFirst(); s.pollFirst(); s.add("c2"); System.out.println(s.size() +" "+ subs.size()); Which are true? (Choose all that apply.) A. The size of s is 4 B. The size of s is 5 C. The size of s is 7 D. The size of subs is 1 E. The size of subs is 2 F. The size of subs is 3 G. The size of subs is 4 H. An exception is thrown at runtime

Answer
B and F are correct. After "g" is added, TreeSet s contains six elements and TreeSet subs contains three (b, c, d), because "g" is out of the range of subs. The first pollFirst() finds and removes only the "a". The second pollFirst() finds and removes the "b" from both TreeSets (remember they are backed). The final add() is in range of both TreeSets. The final contents are [c,c2,d,e,g] and [c,c2,d]. A, C, D, E, G, and H are incorrect based on the above.

Q6.
3. import java.util.*; 4. class Business { } 5. class Hotel extends Business { } 6. class Inn extends Hotel { } 7. public class Travel { 8. ArrayList<Hotel> go() { 9. // insert code here 10. } 11. } Which, inserted independently at line 9, will compile? (Choose all that apply.) A. return new ArrayList<Inn>(); B. return new ArrayList<Hotel>(); C. return new ArrayList<Object>(); D. return new ArrayList<Business>();

Answer: B is correct.
A is incorrect because polymorphic assignments don't apply to generic type parameters. C and D are incorrect because they don't follow basic polymorphism rules.

You might also like