Резюме
Да прегледаме колекциите доставяни със стандартната Java (1.0 и 1.1) библиотека (BitSet не е включена тук понеже е клас за по-специална употреба):
-
Масивът асоциира числени индекси с обектите. Той съдържа обекти от известен тип, така че не е необходимо да се прави каст. Може да бъде многоразмерен и може да съдържа примитиви. Обаче дължината му не може да бъде променяна след като е създаден.
-
Vector също асоциира числени индекси с обекти – маое да мислите за масивите и Vectors като колекции с произволен достъп. Vector автоматически си променя дължината с въвеждането на нови елементи. Но ArrayList може да съдържа само манипулатори на Object, така че не може да съдържа примитиви и трябва да се прави кастинг когато се извлича Object манипулатор от колекцията.
-
Hashtable е тип Dictionary, което е начин да се асоциират не числа, но обекти с други обекти. Hashtable също поддържа произволен достъп до обектите, фактически цялата работа в тях е фокусирана върху бързия достъп.
-
Stack е "проследен влязъл-първи излиза" (LIFO) опашка.
Ако сте запознати със структурите данни, може да се чудите защо няма по-богато множество колекции. От функционална гледна точка нуждаете ли се от повече колекции? С Hashtable може да вмъквате неща и да ги намирате бързо, а с Enumeration може да се движите през колекцията и да извършвате операция над всеки елемент от последователността. Това е мощен инструмент и може би той би трябвало да е достатъчен.
Но Hashtable няма концепция за подреждане. Vectors дават линейно нареждане, но е скъпо да се вмъкне елемент в средата на всеки от тях. В добавка опашките, дековете, опашките с приоритет и дърветата са за подреждане на елементите, не само за слагане и последващо намиране на елементи линейно. Тези структури данни са също полезни, затова са включени в Standard C++. Поради тази причина ще считате колекциите в стандартната Java библиотека само като начална точка и, ако трябва да използвате Java 1.0 или 1.1, използвайте JGL когато се нуждаете от нещо извън това.
Ако може да използвате Java 2 ще използвате само Колекциите на Java 2, които вероятно ще удовлетворят всичките ви нужди. Забележете че повечето от тази книга беше създадена чрез Java 1.1, така че ще видите, че колекциите които са използвани до края на книгата са тези от Java 1.1: Vector и Hashtable. Това е малко болезнено ограничение на моменти, но дава по-добра обратна съвместимост със стар Java код. Ако пишете нов код с Java 2, Колекциите на Java 2 ще ви служат много по-добре.
Упражнения -
Създайте нов клас наречен Gerbil с int gerbilNumber който се инициализира в конструктра (подобно на Mouse примера в тази глава). Дайте му метод наречен hop( ) който извежда кое gerbil число е и че то подскача. Създайте ArrayList и добавете много Gerbil обекти във Vector. Сега използвайте метода elementAt( ) за придвижване през Vector и извикайте hop( ) за всеки Gerbil.
-
Променете упражнение 1 така че да използва Iterator за придвижване през Vector докато се вика hop( ).
-
В AssocArray.java сменете примера така че да използва Hashtable вместо AssocArray.
-
Вземете класа Gerbil от упражнение 1 и го сложете в Hashtable, асоциирайки името на Gerbil като String (ключа) за всеки Gerbil (стойността) които слагате в таблицата. Вземете Iterator за keys( ) и го използвайте за придвижване през Hashtable, оглеждайки Gerbil за всеки ключ и печатайки ключа, казвайки на gerbil да направи hop( ).
-
Променете упражнение 1 в глава 7 да използва ArrayList за съдържане на Rodentи и Iterator за придвижване през последователността от Rodentи. Помнете че ArrayList съдържа само Objects така че трябва да се прави каст (т.е.: RTTI) когато се прави достъп до отделни Rodentи.
-
(Преходно) В глава 7 намелете примера GreenhouseControls.java, който се състои от три файла. В Controller.java класът EventSet е точно колекция. Променете кода да използва Stack вместо EventSet. Това ще изисква повече от само заместването на EventSet със Stack; ще трябва да използвате също и Iterator ца циклене в множеството елементи. Вероятно ще намерите че е по-лесно да третирате на моменти колекцията като Stack и в други моменти като Vector.
-
(Предизвикателство). Намерете сорса на Vector в библиотеката сорс на Java която идва с всички Java дистрибуции. Копирайте този код и направете специална версия наречена intVector която съдържа само int. Проучете какво ще трябва за да се направи специална версия на Vector за всички примитивни типове. Сега проучете какво ще стане ако създадете клас-свързан списък който работи с всички примитивни типове. Ако някога се реализират параметризираните типове в Java, те ще дадат начин да се свърши тази работа автоматично за вас (както и много други ползи).
Основната философия на Java е че “лошо формиран код няма да бъде задействан.”
Както и със C++, идеалния момент да се хване грешка е времето на компилация, преди даже да се опитате да пуснете програмата. Обаче не всички грешки могат да бъдат открити по време на компилация. Определена част проблеми могат да се обработват само по време на изпълнение, по някакъв формализъм, където източникът на грешката трябва да има начин да подаде информация на кода, който ще я обработва, за да може обработката да бъде подходяща.
В C и други ранни езици можеше да съществуват няколко такива формализма, а освен това те бяха задавани като конвенция, а не като част от програмния език. Типично се връщаше стойност и се слагаше флаг, като се предполагаше, че получателят ще провери флага и ще изследва стойността за да разбере проблема. Обаче с течение на годините стана ясно, че програмистите които правят библиотеките имат тенденция да се считат за непобедими, както в: “Да, грешки могат да се появят при другите но не и в моя код.” Така, не много изненадващо, те не правеха нужните проверки (а и понякога ситуациите на грешка бяха твърде тъпи за да се проверява за тях1). Ако бяхте достатъчно последователни да проверявате за грешки всеки път като извикате метод, вашият код рискуваше да се превърне в нечитаем кошмар. Понеже програмистите трябваше да се се оправят с тези програмни системи те упорито поддържаха че: Този подход към обработката на грешки е главното ограничение пред правенето на добри, големи, управляеми програми.
Решението е да се разкара решаването на въпросите според случая от обработката на грешки и да се въведе формализъм. Това фактически има дълга история, понеже реализации на поддръжка на изключения се отнасят и за операционни системи от 1960-те години и даже за on error goto на BASIC. Но поддръжката на изключения в C++ беше базирана на тази в Ada, а в Java е основана предимно на C++ (макар и повече да изглежда на Object Pascal).
Думата “exception” се има пред вид в значението “Вземам изключение за нещо.” В точката на възникване на проблема би могло да не се знае какво да се прави с него, но се знае, че не може просто весело да се продължи; трябва да се спре и някой, някъде, трябва да изясни какво ще се прави. Но вие нямате информация в текущия момент за решаване на проблема. Така че представяте проблема в по-висок контекст така че някой с нужната информация ще вземе мерките (доста подобно на веригата от команди (при командването на нещо, например кораб-бел.пр.)).
Другата голяма полза от поддържането на изключенията е че те очистват кода. Наместо да се проверява за конкретна грешка и това да се прави в няколко места в програмата, повече няма да се проверява в точката на извикване на метода (понеже изключението гарантира, че някой ще го хване). Необходимо е да се обработи проблема само в едно място, тъй наречения exception handler. Това спестява писане и разделя кода за нещата които трябва да се правят от този който е за в случай че се развали нещо. Изобщо да се пише, чете и тества код става много по-ясно като се използват изключения, отколкото по стария начин.
Понеже поддръжката на изключения се налага от компилатора на Java, има толкова много примери в тази книга които могат да се напишат без знание на подробности. Тази глава ви въвежда в нещата, които позволяват да напишете код за правилна обработка на изключенията, а също и как да генерирате свои собствени изключения, ако ваш метод има неприятности.
Сподели с приятели: |