450 likes | 576 Views
Тема 2 . Колекции. Колекции. Темплейти ; Колекции, видове ; Изисквания към класовете, съхранявани в колекции ; Същност. Използване на темплейтни колекции и интерфейси ; Работа с темплейти и потребителско дефиниране на темплейтни класове. Колекции. Дефиниция:
E N D
Колекции • Темплейти; • Колекции, видове; • Изисквания към класовете, съхранявани в колекции; • Същност. Използване на темплейтни колекции и интерфейси; • Работа с темплейти и потребителско дефиниране на темплейтни класове.
Колекции Дефиниция: Колекции (Контейнерни класове) са класове, които съхраняват обекти от произволен тип. Особености: • Не е от значение какъв е типът на обекта, който съхраняват- полиморфизъм ; • Тип на данните в колекцията е java.lang.Object; • Съхраняването на прости типове е проблем; • Типово преобразуване към реалния тип, когато се използват.
Колекции Например: • List съхранява подредена поредица от елементи от класа Object. • Map съхранява асоциации от двойка, съставена от ключ и стойност, които също са от общия базов тип (класа) Object.
Колекции Пример: Date date = new Date( ); List list = new ArrayList( ); list.add( date ); // допуска се произволна // референция към тип //... Date firstElement = (Date)list.get(0);
Колекции Решение: • Преписване на метода на колекцията: public void add( Object o ) { ... } public void add( Date d ) { ... } // припокриващ метод за дата • Написване на собствен DateList, в който да се делегират методите на класа, но да работи с дати. Недостатъци... Collections.sort( )
Шаблонни колекции Темплейтизиране на колекциите от първата група ( след версия 5.0 ). Добавяне на шаблонна променлива E. Примерна спецификация на колекцията List в новата версия : public class List< E > { ... public void add( E element ) { ... } public E get( int i ) { ... } }
Шаблонни колекции Използване на шаблонната променлива: • При деклариране на променливи на инстанциите на класа; • За аргументите на методите; • Връщаните типове от методите. Например: E е аргумент на метода add( ), връщан тип на метода get( ).
Шаблонни колекции Примери за използване: List<String> listOfStrings; List<Date> dates; List<java.math.BigDecimal> decimals; List<Foo> foos; Резултат: public class List< String > { ... public void add( String element ) { ... } public String get( int i ) { ... } }
Шаблонни колекции List<String> listOfStrings = new ArrayList<String>( ); listOfStrings.add("eureka! "); String s = listOfStrings.get(0); // "eureka! " listOfStrings.add( new Date( ) ); // грешка при компилация
Шаблонни колекции Асоциативни колекции С два шаблонни аргумента - K, V . Типовете не могат да са еднакви. Примерна спецификация на колекцията Map в новата версия 5.0 : public class Map< K, V > { ... public V put( K key, V value ) { ... } // връща старата стойност по ключа public V get( K key ) { ... } }
Масиви Особености на масивите: • имат член променлива, само за четене length която връща големината на масива; • По време на изпълнение се проверява и генерира изключение при опит за достъп извън границите на масива; • Всички масиви се създават в динамичната памет и могат да се присвояват един на друг (копира се само референцията към паметта); • При деклариране на масив в Java се декларира само референция към масива (може и нулева). Реалната памет се създава с оператора new ; • началната инициализация на новосъздадения масив с нулеви стойности в зависимост от типа;
Масиви Примери: int [] A; // A е указател (референция към масива) A = new int [10]; // указателя A се инициализира с масив с // големина 10 от типа (int) A[0] = 5; // запис на данни в първия елемент на A int [] A = new int[10]; ... A.length ... // стойност 10 A = new int[20]; ... A.length ... // нова стойност 20 Двумерни масиви: int [][] A = new int[4][3]; int [][] A = new int[4][]; A[1] = new int[4]; A[3] = new int[2];
Масиви Копиране на масиви в Java. Използва се методът на java.lang.System arraycopy . За изпълнението се използва синтаксис, подобен на println. Параметрите са: • src: масив source (масивът от който се копира) • srcPos: начален индекс на масива от който се копира • dst: масив destination (масивът към който се копира) • dstPos: начален индекс на масива към който се копира • count: брой на стойностите за копиране
Масиви Пример за копиране на масиви: int [] A, B; A = new int[10]; // зареждане на масива A B = new int[5]; System.arraycopy(A, 0, B, 0, 5) // копират се 5 стойности от A в B System.arraycopy(A, 9, B, 4, 1) // копира се последната стойност на A // в последната на B
Интерфейс Collection Същност: Родител на всяка колекция е интерфейса Collection . Интерфейса не определя вътрешната организация на елементите, негови членове, не указва има ли дублиране на елементи, има ли подредба на елементите и др. Тези детайли на имплементацията са оставени за наследниците. В него са дефинирани най-общите за всички методи:
Интерфейс Collection Основни методи • Добавяне: public boolean add( element ); • Премахване: public boolean remove( element ); • Проверка: public boolean contains( element ); • Брой елементи: public int size( ); • Има ли елементи: public boolean isEmpty( ); • Връща обекта итератор, който позволява достъп до елементите: public Iterator iterator( ); • Между колекции: addAll( ), removeAll( ), containsAll( )
Темплейтен Интерфейс Collection Същност: Позволява да се параметризира типа на интерфейса за темплейтната променлива от определен тип (Date): Collection<Date> dates = new ArrayList<Date>( ); dates.add( new Date( ) ); dates.add( "foo" ); // грешка при компилация!! Компилаторът проверява за съответствие на типа и продуцира грешка. Например непроверено е добавянето: Collection dates = new ArrayList( ); dates.add( new Date( ) );
Преобразуване на колекция към масив и обратно Същност: Преобразуване на колекция в масив е специална конвенция на езика, която позволява съдържанието на колекцията да се копира в масив. • Шаблонен вариант: public Object[] toArray( ); public <E> E[] toArray( E[] a ); • Нешаблонен вариант: public Object[] toArray( Object [] a ); • Обратно преобразуване. Използва се статичния метод asList на класа java.util.Arrays : String [] myStrings = myCollection( new String[0] ); List list = Arrays.asList( myStrings );
Итериране на колекции Става с обекта iterator предоставен от java.util.Iterator. Основни методи: • public Object next( ) - следващия елемент на колекцията; • public boolean hasNext( ) - връща true ако може да се извика next( ), за да се получи следващия елемент. • public void remove( ) - премахване на последния върнат от next( ) елемент на колекцията;
Итериране на колекции Пример: • Вариант с while public void printElements(Collection c, PrintStream out) { Iterator iterator = c.iterator( ); while ( iterator.hasNext( ) ) out.println( iterator.next( ) ); } • Вариант с for public static void printElements(Collection c) { for(Iterator x = c.iterator(); x.hasNext();) System.out.println(x.next().toString() + " "); }
Итериране на колекции Допуска се и общия вариант на цикъл за достъп до цялата колекция. Например, може да се премине през цялата колекция от елементи с операторния вариант на for: Collection<Date> collection = ... for( Date date : collection ) System.out.println( date ); Предоставя се само за типа Collection, не и за Map, защото в него се съхраняват двойки и няма еднозначност на елемента, който се достъпва (ключа или стойността).
Колекции (Интерфейс изброимост ) Интерфейс изброимост се предоставя от java.util.Enumeration Enumeration е итераторен интерфейс, който има подобни имена на методи: nextElement( ) и hasMoreElements( ) но прави същото. По старите класове са предоставяли този интерфейс в случаите, където в по-новите се използва итератор. Има възможност за преобразуване на изброимия интерфейс в списък със статични методи на класа java.util.Collections: List list = Collections.list( enumeration );
Колекции-видове Интерфейсът Collection има два основни интерфейса-наследници. • Set представя колекция, в която не се допуска дублиране на елементите; • List е колекция, чиито елементи са подредени последователно;
Колекция Set Същност: Set (Интерфейс) имплементира методите на Collection. Той осигурява правило за недопускане на дублиране. Ако се направи опит за извикване на метода add( ) при наличие на елемента в колекцията, резултатът е false.
Колекция SortedSet SortedSet(Интерфейс) е модификация, която добавя няколко метода към Set. Промени: При извикване на add() и remove() се поддържа реда на елементите. Добавени методи: • Създаване на подмножества subSet( ); • Глава headSet( ); • Опашка tailSet( ); • Методите first( ), last( ) и comparator( ) връщат първи, последен и обектът, използван за сравнение на елементите.
Колекция List Същност : List е линейна колекция (Интерфейс ), подобна на масив с методи за промяна на позицията на елементите.
Колекция List Основни методи: • public void add( E element ) добавя елемент в края на списъка; • public void add( int index, E element ) вмъква елемент на позицията, която е параметър. Ако е по-малка от нула или по-голяма от дължината се предизвиква изключение IndexOutOfBoundsException. Следващите елементи се изместват с позиция след вмъкнатия на индекса index. • public void remove( int index ) премахва елемента на указаната позиция; • public E get( int index ) връща елемeнта на указаната позиция; • public Object set( int index, E element ) промяна на елемента на указаната позиция със специфицирания като параметър. Трябва да съществува такъв или се предизвиква IndexOutOfBoundsException.
Колекция Map Същност: Интерфейс, включен в пакета java.util.Map. Предназначение: За съхраняване на колекции от двойки ключ/стойност. Това са изветните съхранители “речник” или “асоциативен масив”. Те поддържат и достъпват елементите чрез ключовата стойност. При запис на елемент в контейнера се асоциира записаната стойност към ключа.При темплейтезирания вариант Map се създава с два параметризирани типа-за ключа и за стойността.
Колекция Map Примери: • Параметризиран (шаблонен): Map<String, Date> dateMap = new HashMap<String, Date>( ); dateMap.put( "today", new Date( ) ); Date today = dateMap.get( "today" ); • Непараметризиран вариант: Map dateMap = new HashMap( ); dateMap.put( "today", new Date( ) ); Date today = (Date)dateMap.get( "today" );
Колекция Map Методи: • public V put( K key, V value) - добавяне на двойка ключ/стойност; • public V get( K key ) получаване на стойност, съответстваща на ключа; • public V remove( K key ) премахване на стойност, съответстваща на ключа; • public int size( ) – брой на двойките ключ/стойност; • public Set keySet( ) – получаване в Set на всички ключове на обекта; • public Collection values( ) – получаване в Collection от всички стойности на обекта (може да има дублирани елементи);
Колекция SortedMap Map има един наследен интерфейс - SortedMap. Той се различава по това, че поддържа елементите си сортирани по реда на ключовата стойност. Предоставя методите subMap( ), headMap( ) и tailMap( ) за получаване на подмножества от сортирания map. Предоставя също метод comparator( ) който връща сравнителя за сортиране. Тъй като поддържа две стойности, има два отделни вида итератора – за ключа и за стойността.
Колекции - обобщение Използването на контейнерите на езика за съхраняване на обекти от произволен клас се основава на класовата йерархия с базов клас Object. За да се осигури организация на обектите в колекциите се използват методите на базовия клас Object. Те се предефинират в класовете за съхраняване в колекции и осигуряват съответните методи за достъп на колекцията.
Колекции - обобщение Общи изисквания и използване на интерфейсите: Множество Set: • Set - Всеки елемент да е уникален, в противен случай не се добавя ; • Обектите, които се добавят към Set трябва да предефинират метода на Object equals( ), за да се осигури уникалността; • Set има еквивалентен интерфейс като Collection; • При Set не се гарантира подредба на елементите.
Колекции - обобщение Хеширано множество HashSet: • HashSet се използва за представяне на множества с изключение на много малки; • Обектите трябва да осигуряват метода hashCode( ); Множество като масив ArraySet: • ArraySet е множество, което може да се използва като масив; • Проектирано е за много малки множества, особено за често създавани и унищожавани • За малки множества, създаването и итерирането е по-бързо от това за HashSet; • Ефективността се намалява при големи множества спрямо Set • Не се изисква HashCode( ).
Колекции - обобщение Множество като дърво TreeSet: • TreeSet подредена версия на Set като двоично дърво; • Изисква имплементация на метода Comparable.compareTo(...) на Object
Имплементиране на сравнение равенство Същност: Базовият клас на всички класове в езика Java-kласът Objectпредоставя интерфейсен метод за определяне на операцията равенство при сравнение за равенство на два обекта от потребителския клас. Методът е предикатен-връща логическа стойност от изпълнението си. Предназначение: За сравняване на обекти. Формат: booleanequals(Object toCompare) Методът се предефинира в потребителския клас с цел да се използва операцията при съхраняването на обектите от класа в контейнерите и търсене на обекти. Допуска се като параметър да се използва обект от класа, а не базовия клас. Може да се предефинира конкретно за класа по следния начин: public boolean equals(CStudent toCompare)
Имплементиране на сравнение равенство Изисквания към функцията, реализираща метода: • рефлексивна - за всяка референция x x.equals(x) да е винаги истина: if (this == obj)return true; • симетрична- за всяка референция x и y, x.equals(y) е true тогава и само тогава, когато y.equals(x) е true; • транзитивана - за всяка референция x, y и z, ако x.equals(y) е true и y.equals(z) е true, тогава x.equals(z) е true. • консистентна – една функция е консистентна, ако за всяка референция на обектите-x и y многократните извиквания на x.equals(y) винаги връща един и същ резултат от сравнението (не предизвиква вътрешни състояния); • Различни класове са несравними: if (getClass() != obj.getClass()) return false; • Ако базовите им класове са различни, са различни: if(!super.equals(obj)) return false; • сравнение с нулева референция. За всяка референция към обект obj, която е ненулева obj.equals(null) е винаги false: if (obj == null) return false;
Имплементиране на сравнение равенство Пример-Предефиниране на сравнение за равенство между обектите от класа студент, която имплементира равенството на студентите по факултетен номер: public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; if(!super.equals(obj)) return false; final Student other = (Student) obj; if (strFacNumer == null) { if (other.strFacNumer != null) return false; } else if (!strFacNumer.equals(other.strFacNumer)) return false; return true; }
Имплементиране на сравнение за подредба Същност: В дефиницията на класа, който ще се съхранява в сортирана колекция трябва да се обяви интерфейса, с който да се сравняват обектите при подредбата им в контейнера. Това става с обявяването като наследник на класа-интерфейса Comparable: public class Student extends Person implements Comparable{…} Имплементиране: Функцията от този интерфейс, която трябва да се предефинира е compareTo: compareTo е с резултат int и има смисъла на сравнител, връщащ 1 (по-малко), -1 (по-голямо) и 0 (равно).
Имплементиране на сравнение за подредба Пример за предефиниране за сравняване на студенти по факултетен номер в нарастващ и намаляващ ред. Използва се сравнението на тип String, което е организирано на същия принцип. Добавят се методи за сравнението: Примери: public int compareTo(Object o) { return (strFacNumer.compareTo(((Student)o).strFacNumer)); } За подредба в нарастващ ред може да се използва обратното сравнение: public int compareTo(Object o) { return (((Student)o).strFacNumer.compareTo(strFacNumer)); }
Създаване на класове, съхраняващи колекции Пример (TreeSet): publicclass CollectionSamples { public Set oColl = new TreeSet(); //Създаване на множество public CollectionSamples(String fileName ) { // Конструктор от файл try { RandomAccessFile myFile = new RandomAccessFile(fileName,"r"); String[] res = new String[4]; //формат: Ivanov m mr. 056001 String strLine; while((strLine = myFile.readLine()) != null ) { res = strLine.split(" "); Object oPersonExpl = new Student(res[0],res[1],res[2],res[3]); oColl.add(oPersonExpl); } myFile.close(); } catch (FileNotFoundException e) {/* да се обработи File Not Found */ } catch(EOFException e) {/* да се обработи End of file */ } catch (IOException e){/* да се обработи Input/Output Error */ } }
Създаване на класове, съхраняващи колекции /* Метод за итериране на колекцията и обработка на информацията-извеждане на системния изход. */ public void printColl(Collection c) { for(Iterator It = c.iterator(); It.hasNext();) System.out.println(It.next().toString()); } public static void main(String[] args) { CollectionSamples obj = new CollectionSamples("Input.txt"); obj.printColl(obj.oColl); } } // public class CollectionSamples
Създаване на класове, съхраняващи колекции Използването на общия интерфейс като формален параметър позволява да се промени типа на колекцията с промяна на полето, например, може да се промени: public Set oColl = new TreeSet(); със следните дефиниции: public Collection oColl = new ArrayList(); // За създаване на колекция от ArrayList public Collection oColl = new Vector(); // За създаване на колекция от класа Vector public Collection oColl = new Stack(); //За създаване на колекция от класа Stack public Collection oColl = new LinkedList(); //За създаване на колекция от класа LinkedList public List oColl = new ArrayList();// За създаване на списък от класа ArrayList public List oColl = new Vector();// За създаване на списък от класа Vector public List oColl = new Stack();// За създаване на списък от класа Stack public List oColl = new LinkedList();// За създаване на списък от класа LinkedList public Set oColl = new HashSet();//За създаване на множество от класа HashSet