За мен колекциите са един от най-мощните инструменти за програмиране. Може да сте забелязали, че съм малко разочарован от колекциите на Java до версия 1.1. Като резултат огромно удоволствие бе да видя, че в Java 2 на колекциите бе обърнато необходимото внимание, те бяха цялостно преработени (от Joshua Bloch от Sun). Считам че колекциите на Java 2 са едната главна черта на версията (другата е Swing библиотеката, разгледана в глава 13) понеже те значително увеличават програмистките мускули и извеждат Java на една линия с по-утвърдени програмни системи.
Част от преработката прави нещата по-свързани и смислени. Например вного имена са по-къси, по-изразителни, по-лесни за разбиране и четене, както и за писане. Някои имена са променени за съгласуване с приетата терминология: мой любим пример е “iterator”вместо “enumeration.”
Преработена е също функционалността на библиотеката на колекциите. Сега може да се получи поведение на свързан списък, опашки и декове (опашки с два края).
Проектирането на библиотека колекции е трудно (както повечето проблеми свързани с библиотеки). В C++ STL се основава на много различни класове. Това е по-добре от наличното преди STL (нищо), но не се вписваше добре в Java. Резултатът беше малко смущаващо блато от класове. В другата крайност аз съм виждал библиотека, състояща се от един клас, “collection,” който работи като ArrayList и HashMap едновременно. Проектантите на колекциите в библиотеката на Java 2 искаха да постигнат баланс: пълната функционалност, която се иска от редовна библиотека, но по-голяма леснота за използване отколкото STL и други подобни библиотаки. Резултатът може да изглежда малко недобър на места. За разлика от решенията приети в ранните Java тези неуспехи не са нещастни случаи, а внимателно подбрани компромиси с цената на ефективността. Може да отнеме повече време да свикнете с някои аспекти на библиотеките, но мисля че бързо ще приемете и започнете да използвате тези нови инструменти.
Колекциите на Java 2 библиотеката вземат въпроса за “владеене на вашите обекти” и го разделят между две различни концепции:
-
Collection: група от отделни елементи, често с прилагане на някакво правило към тях. List трябва да държи елементите на конкретна последователност, а Set не може да има дублиращи се елементи. (Bag, което не е реализирано в Collections библиотеката на Java 2 поради това, че Listовете дават тази функционалност, няма такива правила.)
-
Map: група от двойки ключ-стойност (каквито сте виждали досега като Hashtable). На пръв поглед това трябва да бъдат Collectionи от двойки, но когато се опитате да го реализирате по този начин става тромаво, така че е по-добре да бъде отделна концепция. От друга страна, удобно е да се преглеждат части от Map чрез създаване на Collection да представя съответната порция. Така Map може да връща Set от своите ключове, List от своите стойности или List от своите двойки. Mapовете, както масивите, могат лесно да бъдат разширени до нови стойности без необходимост от нови концепции: просто правите Map чиито стойности са Mapове (и стойностите на тези Mapове могат да бъдат Mapове и т.н.).
Collectionите и Mapовете могат да бъда прилагани по много различни начини, съответно на програмистките нужди. Полезно е да се погледне диаграмата на колекциите на Java 2:
…
![](6732_html_704aa66c.gif) Тази диаграма може да изглежда замайващо сложна на пръв поглед, но до края ня главата ще видите, че има само три класа колекции: Map, List и Set и само две или три реализации на всяка една (с, типично, предпочитана версия). Когато това стане колекциите на Java 2 не би трявало да изглеждат толкова заплашително.
Кутийките с прекъсната линия представляват interfaceите, с линия от точки — abstract класовете, а с непрекъсната линия са нормалните (конкретни) класове. Стрелките с прекъсната линия показват че даден клас прилага даден interface (или в случая на abstract клас, частично прилага този interface). Стрелките с двойна линия показват, че може да се правят обекти от сочения клас. Например всяка Collection може да прави Iterator, докато List може да прави ListIterator (както и обикновен Iterator, тъй като List е наследен от Collection).
Интерфейсите свързани с владеенето на обекти са Collection, List, Set и Map. Типично ще е да пишете по-голямата част от кода си да разговаря с тези интерфейси, а единственото място където точния тип ще е необходим да бъде точката на създаване на обект. Така че може да създадете List по този начин:
List x = new LinkedList();
Разбира се, бихте могли да решите да направите x LinkedList (вместо родов List) и да дадете точна информация за типа на x. Красотата (и целта) на използването на interface е че ако решите да промените реализацията, всичко което ще е необходимо е да смените типа в точката на създаването, както тук:
List x = new ArrayList();
Останалата част от кода може да остане незасегната.
В йерархията на класовете може да се видят множество класове, чиято декларация започва с “Abstract,” а това може да е смущаващо отначало. Те са просто инструменти, които частично прилагат даден интерфейс. Ако правехте ваш собствен Set, например, не бихте започнали с интерфейса на Set и реализация на всички методи, вместо това бихте наследили от AbstractSet и сторили минимално необходимото за създаване на новия клас. Обаче Колекциите в библиотеката на Java 2 съдържат достатъчно функционалност за удовлетворяване на нуждите ви практически за всичко. Така че за вашите цели бихте могли да игнорирате всичко, което започва с “Abstract.”
Поради това, както гледате диаграмата, ви засягат само онези interfaceи от горната част на диаграмата и конкретните класове (онези с непрекъсната линия). Типично ще правите обекти от конкретни класове, ъпкаст към съответния interface, а после ще използвате interface-а в останалата част от вашия код. Ето прост пример, който попълва Collection със String обекти и после извежда всеки обект от тази Collection:
//: c08:newcollections:SimpleCollection.java
// A simple example using Java 2 Collections
package c08.newcollections;
import java.util.*;
public class SimpleCollection {
public static void main(String[] args) {
Collection c = new ArrayList();
// Upcast because we just want to
// work with Collection features
for(int i = 0; i < 10; i++)
c.add(Integer.toString(i));
Iterator it = c.iterator();
while(it.hasNext())
System.out.println(it.next());
}
} ///:~
Всичките примери за Java 2 Collections библиотеките ще трябва да се сложат в поддиректория newcollections, така че да се напомни че ще работят само с Java 2. Като резултат може да стартирате програмата така:
java c08.newcollections.SimpleCollection
с подобен синтаксис за другите програми от главата.
Може да се види че Java 2 Collections са част от java.util библиотеката, така че няма нужда от никакви допълнителни import оператори за използването им.
Първата линия в main( ) създава ArrayList и после го превръща към Collection. Тъй като този пример използва само методите на Collection всеки обект наследен от Collection би работил, но ArrayList е типичният работен кон на Collection и заема мястото на Vector.
Методът add( ) , както показва името му, добавя нов елемент към Collection. Обаче документацията грижливо твърди че add( ) “осигурява че тази Collection съдържа посочения елемент.” Това е за да се позволи в смисъла на Set, който добавя елемент само ако още няма такъв добавен. С ArrayList, или който и да е сорт List, add( ) винаги значи “пъхни го вътре.”
Всички Collectionи могат да дадат Iterator чрез техния iterator( ) метод. Iterator е точно като Enumeration, който замества, освен че:
-
Използва име (iterator) което е исторически признато и прието в ООП общността.
-
Използва по-къси имена на методи от Enumeration: hasNext( ) вместо hasMoreElements( ), next( ) вместо nextElement( ).
-
Въвежда нов метод, remove( ), който маха последния елемент даден от Iterator. Така че може да извикате remove( ) само веднъж сле всяко извикване на next( ).
В SimpleCollection.java може да видите че Iterator е създаден и използван за работа с Collection, извеждайки всеки елемент.
Сподели с приятели: |