Книга 1 - Начальные сведения
Книга 2 - Более профессиональный подход |
Студенческий отдел кадров Что такое коллекции - краткое описаниеКак уже можно было видеть из описания классов, класс УправляющаяСистема использует два списка - список групп и список студентов. как видите в нашем крайне скудном приложении мы уже столкнулись с ситуацией, когда нам потребуется работать с группой/массивом/списокм данных. Буем называть это коллекцией.
Рекомендуем: Здесь не будет приведена подробная информация о коллекциях.
Для хорошего ознакомления с ними отсылаем вас к документации по JAVA.
В первую очередь это оригинал от SUN:
The Collections Framework
Хоть я и говорил, что мы не будем рассматривать очень подробно коллекции, все же считаю необходимым
привести некоторую информацию.
Типов коллекций достаточно много. Это связано с тем, что пока никто не смог создать коллекцию,
которая прекрасно подходила бы для всех видов работы с ней. Например, если коллекция позволяет быстро
добавлять в нее новый элемент вне зависимости от размера коллекции, то скорость обращения к какому-то
элементу этой коллекции по индексу скоре всего будет зависеть от размера. И чем больше будет ваша
коллекция, тем больше потребуется времени для этого. Такие коллекции обычно организуются как связанные
списки, где первый элемент ссылается на второй, второй - на третий и т.д.
До версии Java 1.5 коллекции не были типизированы - т.е. программист мог поместить в коллекцию любой
объект. Таким образом в коллекции могли мирно уживаться строки, даты и другие объекты разных классов.
Что на самом деле встречается достаточно редко - чаще в коллекции находятся данные одного типа.
Версия Java 1.5 ввела понятие Generic. Об этом вы можете более подробно
узнать в статье Что нового в Java SE 5.0. Основная мысль в следующем:
программист при объявлении коллекции указывает, какой тип данных может в ней храниться. И с этого момента
уже на уровне компиляции есть проверка, что в коллекции будут данные определенного типа. Это позволяет
не писать код приведения типа в виде String s = (String)list.get(0);. // Предположим, что мы как-то получили список групп List groups = getGroups(); ... for (Iterator gi = groups.iterator(); gi.hasNext();) { Group group = (Group) gi.next(); System.out.println("---> Группа:" + group.getNameGroup()); }
Обратите внимание, что мы должны использовать класс Iterator.
Это специальный класс, который позволяет работать с коллекций. Идея его очень простая - при создании
итератор указывает на начало коллекции и перемещается к следующему элементу вызовом метода
next. Этот метод также возвращает тот объект из коллекции,
на который в данный момент указывает итератор. А метод hasNext
проверяет - есть ли еще элементы в коллекции. Таким образом просмотреть ЛЮБУЮ коллекцию.
Также мы вынуждены приводить итератор к нужному нам типу насильно.
Что не может не приводить иногда к ошибкам. List<Group> groups = getGroups(); // Предположим, что мы как-то получили список групп ... for (Group group : groups) { System.out.println("---> Группа:" + group.getNameGroup()); } Как говориться - почуствуйте разницу. Во-первых, мы сразу говорим, что наш список содержит объекты типа Gruop. Это позволяет нам больше не думать о приведении типа при работе со списком. Компилятор не позволит нам добавлять объекты иного класса, автоматически сделает приведение - в общем всякие вкусности. Во-вторых - обратите внимание на новый вариант прохода по списку - цикл for теперь совсем простой и достаточно симпатично выглядит. По секрету - вы все еще можете использовать Iterator - и он будет работать точно также. Только теперь итератор тоже можно типизировать - например Iterator<Group>. И теперь метод next будет возвращать нужный нам тип данных сразу без приведения. // Предположим, что мы как-то получили список групп List groups = getGroups(); ... for (Iterator<Group> gi = groups.iterator(); gi.hasNext();) { Group group = gi.next(); System.out.println("---> Группа:" + group.getNameGroup()); } Подводя итог краткому описанию возможностей коллекций я призываю вас очень внимательно отнестись к этому очень мощному инструменту обработки данных. В подавляющем большинстве приложений вам они потребуются обязательно.
А теперь попробуем написать несложную реализацию классов для нашей системы. Наши классы будут
находится в следующих файлах JAVA Замечание: Для класса ManagementSystem сделаем метод main, который решает две задачи:
Классы Student и Group - эти классы содержат только описания необходимых полей, а также методы для доступа к этим полям. Так называемые сеттеры и геттеры (от слов set/get - установить/получить). Это уже по сути стандартный подход к работе с полями класса - поле для хранения объявляется как private, а для доступа используются методы set/get. Принцип именования следующий: Имя переменной начинается со строчной буквы – например, ИД студента будет выглядеть как studentId. А методы будут выглядеть вот так: private int studentId; public void setStudentId(int studentId) { this.studentId = studentId; } public int getStudentId() { return this.studentId; }
Как видите, методы имеют слова set/get и дальше уже с заглавной
буквы идет имя переменной. Также при установке поля в метод передается параметр такого же типа.
Я люблю именовать его так же, хотя это не обязательно. Многие системы разработки
(Eclipse, JBuilder, IDEA) содержат специальные меню, которые позволяют создать set/get
автоматически по списку переменных. Такой принцип именования был предложен SUN для того, чтобы
можно было легко определять, какие свойства и методы есть у класса - об этом подробно описывается
в спецификации JavaBeans.
Для того, чтобы студенты имели "возможность сравниваться друг с другом" мы должны реализовать
интерфейс Comparable, который имеет только один метод - import java.text.Collator; import java.util.Locale; public class Test { public static void main(String[] arg) { Collator c = Collator.getInstance(new Locale("ru")); // Если закомментировать следующую строку, то "ПАВЕЛ" будет не равен "Павел" // (c.compare!=0) что по идее не совсем корректно c.setStrength(Collator.PRIMARY); System.out.println(c.compare("ПАВЕЛ", "ПАВЕЛ")); System.out.println(c.compare("ПАВЕЛ", "Павел")); System.out.println(c.compare("ПАВЕЛ", "артем")); System.out.println("ПАВЕЛ".compareTo("артем")); } } А теперь приведем код наших классов с комментариями:
Student.java package students.logic; import java.text.Collator; import java.text.DateFormat; import java.util.Date; import java.util.Locale; public class Student implements Comparable { // поле ИД СТУДЕНТА private int studentId; // поле ИМЯ private String firstName; // поле ФАМИЛИЯ private String surName; // поле ОТЧЕСТВО private String patronymic; // поле ДАТА РОЖДЕНИЯ private Date dateOfBirth; // поле ПОЛ private char sex; // поле ИД ГРУППЫ private int groupId; // поле ГОД ОБУЧЕНИЯ private int educationYear; // get/set для ДАТА РОЖДЕНИЯ public Date getDateOfBirth() { return dateOfBirth; } public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; } // get/set для ГОД ОБУЧЕНИЯ public int getEducationYear() { return educationYear; } public void setEducationYear(int educationYear) { this.educationYear = educationYear; } // get/set для ИД ГРУППЫ public int getGroupId() { return groupId; } public void setGroupId(int groupId) { this.groupId = groupId; } // get/set для ИД СТУДЕНТА public int getStudentId() { return studentId; } public void setStudentId(int studentId) { this.studentId = studentId; } // get/set для ИМЯ public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } // get/set для ОТЧЕСТВО public String getPatronymic() { return patronymic; } public void setPatronymic(String patronymic) { this.patronymic = patronymic; } // get/set для ФАМИЛИЯ public String getSurName() { return surName; } public void setSurName(String surName) { this.surName = surName; } // get/set для ПОЛ public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } // DateFormat - класс для преобразования даты // в строку в определеннном формате. // Подробнее смотрите документацию по этому методу public String toString() { return surName + " " + firstName + " " + patronymic + ", " + DateFormat.getDateInstance(DateFormat.SHORT).format(dateOfBirth) + ", Группа ИД=" + groupId + " Год:" + educationYear; } public int compareTo(Object obj) { Collator c = Collator.getInstance(new Locale("ru")); c.setStrength(Collator.PRIMARY); return c.compare(this.toString(), obj.toString()); } }
Group.java package students.logic; public class Group { // поле ИД ГРУППЫ private int groupId; // поле ИМЯ ГРУППЫ private String nameGroup; // поле КУРАТОР private String curator; // поле СПЕЦИАЛЬНОСТЬ private String speciality; // get/set для КУРАТОР public String getCurator() { return curator; } public void setCurator(String curator) { this.curator = curator; } // get/set для ИД ГРУППЫ public int getGroupId() { return groupId; } public void setGroupId(int groupId) { this.groupId = groupId; } // get/set для ИМЯ ГРУППЫ public String getNameGroup() { return nameGroup; } public void setNameGroup(String nameGroup) { this.nameGroup = nameGroup; } // get/set для СПЕЦИАЛЬНОСТЬ public String getSpeciality() { return speciality; } public void setSpeciality(String speciality) { this.speciality = speciality; } public String toString() { return nameGroup; } }
Давайте немного подробнее остановимся на ManagementSystem.java.
Этот класс пока не очень сложный, но все таки он дает нам некоторую функциональность.
Он позволяет добавить нового студента, изменить параметры студента, перевести всех студентов
одной группы в другую (частая ситуация, когда после года обучения студенты переходят в другую
группу), удалить студентов из группы - для тех, кто закончил учиться.
После кода мы еще остановимся на некоторых функциях.
ManagementSystem.java package students.logic; import java.io.FileNotFoundException; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.TreeSet; public class ManagementSystem { private List<Group> groups; private Collection<Student> students; // Для шаблона Singletone статическая переменная private static ManagementSystem instance; // закрытый конструктор private ManagementSystem() { loadGroups(); loadStudents(); } // метод getInstance - проверяtт, инициализирована ли статическая // переменная (в случае надобности делает это) и возвращает ее public static synchronized ManagementSystem getInstance() { if (instance == null) { instance = new ManagementSystem(); } return instance; } // Метод, который вызывается при запуске класса public static void main(String[] args) { // Этот код позволяет нам перенаправить стандартный вывод в файл // Т.к. на экран выводится не совсем удобочитаемая кодировка, // файл в данном случае более удобен try { System.setOut(new PrintStream("out.txt")); } catch (FileNotFoundException ex) { ex.printStackTrace(); return; } ManagementSystem ms = ManagementSystem.getInstance(); // Просмотр полного списка групп printString("Полный список групп"); printString("*******************"); List<Group> allGroups = ms.getGroups(); for (Group gi : allGroups) { printString(gi); } printString(); // Просмотр полного списка студентов printString("Полный список студентов"); printString("***********************"); Collection<Student> allStudends = ms.getAllStudents(); for (Student si : allStudends) { printString(si); } printString(); // Вывод списков студентов по группам printString("Список студентов по группам"); printString("***************************"); List<Group> groups = ms.getGroups(); // Проверяем все группы for (Group gi : groups) { printString("---> Группа:" + gi.getNameGroup()); // Получаем список студентов для конкретной группы Collection<Student> students = ms.getStudentsFromGroup(gi, 2006); for (Student si : students) { printString(si); } } printString(); // Создадим нового студента и добавим его в список Student s = new Student(); s.setStudentId(5); s.setFirstName("Игорь"); s.setPatronymic("Владимирович"); s.setSurName("Перебежкин"); s.setSex('М'); Calendar c = Calendar.getInstance(); c.set(1991, 8, 31); s.setDateOfBirth(c.getTime()); s.setGroupId(1); s.setEducationYear(2006); printString("Добавление студента:" + s); printString("********************"); ms.insertStudent(s); printString("--->> Полный список студентов после добавления"); allStudends = ms.getAllStudents(); for (Student si : allStudends) { printString(si); } printString(); // Изменим данные о студенте - Перебежкин станет у нас Новоперебежкиным // Но все остальное будет таким же - создаем студента с таким же ИД s = new Student(); s.setStudentId(5); s.setFirstName("Игорь"); s.setPatronymic("Владимирович"); s.setSurName("Новоперебежкин"); s.setSex('М'); c = Calendar.getInstance(); c.set(1991, 8, 31); s.setDateOfBirth(c.getTime()); s.setGroupId(1); s.setEducationYear(2006); printString("Редактирование данных студента:" + s); printString("*******************************"); ms.updateStudent(s); printString("--->> Полный список студентов после редактирования"); allStudends = ms.getAllStudents(); for (Student si : allStudends) { printString(si); } printString(); // Удалим нашего студента printString("Удаление студента:" + s); printString("******************"); ms.deleteStudent(s); printString("--->> Полный список студентов после удаления"); allStudends = ms.getAllStudents(); for (Student si : allStudends) { printString(si); } printString(); // Здесь мы переводим всех студентов одной группы в другую // Мы знаем, что у нас 2 группы // Не совсем элегантное решение, но пока сделаем так Group g1 = groups.get(0); Group g2 = groups.get(1); printString("Перевод студентов из 1-ой во 2-ю группу"); printString("***************************************"); ms.moveStudentsToGroup(g1, 2006, g2, 2007); printString("--->> Полный список студентов после перевода"); allStudends = ms.getAllStudents(); for (Student si : allStudends) { printString(si); } printString(); // Удаляем студентов из группы printString("Удаление студентов из группы:" + g2 + " в 2006 году"); printString("*****************************"); ms.removeStudentsFromGroup(g2, 2006); printString("--->> Полный список студентов после удаления"); allStudends = ms.getAllStudents(); for (Iterator i = allStudends.iterator(); i.hasNext();) { printString(i.next()); } printString(); } // Метод создает две группы и помещает их в коллекцию для групп public void loadGroups() { // Проверяем - может быть наш список еще не создан вообще if (groups == null) { groups = new ArrayList<Group>(); } else { groups.clear(); } Group g = null; g = new Group(); g.setGroupId(1); g.setNameGroup("Первая"); g.setCurator("Доктор Борменталь"); g.setSpeciality("Создание собачек из человеков"); groups.add(g); g = new Group(); g.setGroupId(2); g.setNameGroup("Вторая"); g.setCurator("Профессор Преображенский"); g.setSpeciality("Создание человеков из собачек"); groups.add(g); } // Метод создает несколько студентов и помещает их в коллекцию public void loadStudents() { if (students == null) { // Мы используем коллекцию, которая автоматически сортирует свои элементы students = new TreeSet<Student>(); } else { students.clear(); } Student s = null; Calendar c = Calendar.getInstance(); // Вторая группа s = new Student(); s.setStudentId(1); s.setFirstName("Иван"); s.setPatronymic("Сергеевич"); s.setSurName("Степанов"); s.setSex('М'); c.set(1990, 3, 20); s.setDateOfBirth(c.getTime()); s.setGroupId(2); s.setEducationYear(2006); students.add(s); s = new Student(); s.setStudentId(2); s.setFirstName("Наталья"); s.setPatronymic("Андреевна"); s.setSurName("Чичикова"); s.setSex('Ж'); c.set(1990, 6, 10); s.setDateOfBirth(c.getTime()); s.setGroupId(2); s.setEducationYear(2006); students.add(s); // Первая группа s = new Student(); s.setStudentId(3); s.setFirstName("Петр"); s.setPatronymic("Викторович"); s.setSurName("Сушкин"); s.setSex('М'); c.set(1991, 3, 12); s.setDateOfBirth(c.getTime()); s.setEducationYear(2006); s.setGroupId(1); students.add(s); s = new Student(); s.setStudentId(4); s.setFirstName("Вероника"); s.setPatronymic("Сергеевна"); s.setSurName("Ковалева"); s.setSex('Ж'); c.set(1991, 7, 19); s.setDateOfBirth(c.getTime()); s.setEducationYear(2006); s.setGroupId(1); students.add(s); } // Получить список групп public List<Group> getGroups() { return groups; } // Получить список всех студентов public Collection<Student> getAllStudents() { return students; } // Получить список студентов для определенной группы public Collection<Student> getStudentsFromGroup(Group group, int year) { Collection<Student> l = new TreeSet<Student>(); for (Student si : students) { if (si.getGroupId() == group.getGroupId() && si.getEducationYear() == year) { l.add(si); } } return l; } // Перевести студентов из одной группы с одним годом обучения в другую группу с другим годом обучения public void moveStudentsToGroup(Group oldGroup, int oldYear, Group newGroup, int newYear) { for (Student si : students) { if (si.getGroupId() == oldGroup.getGroupId() && si.getEducationYear() == oldYear) { si.setGroupId(newGroup.getGroupId()); si.setEducationYear(newYear); } } } // Удалить всех студентов из определенной группы public void removeStudentsFromGroup(Group group, int year) { // Мы создадим новый список студентов БЕЗ тех, кого мы хотим удалить. // Возможно не самый интересный вариант. Можно было бы продемонстрировать // более элегантный метод, но он требует погрузиться в коллекции более глубоко // Здесь мы не ставим себе такую цель Collection<Student> tmp = new TreeSet<Student>(); for (Student si : students) { if (si.getGroupId() != group.getGroupId() || si.getEducationYear() != year) { tmp.add(si); } } students = tmp; } // Добавить студента public void insertStudent(Student student) { // Просто добавляем объект в коллекцию students.add(student); } // Обновить данные о студенте public void updateStudent(Student student) { // Надо найти нужного студента (по его ИД) и заменить поля Student updStudent = null; for (Student si : students) { if (si.getStudentId() == student.getStudentId()) { // Вот этот студент - запоминаем его и прекращаем цикл updStudent = si; break; } } updStudent.setFirstName(student.getFirstName()); updStudent.setPatronymic(student.getPatronymic()); updStudent.setSurName(student.getSurName()); updStudent.setSex(student.getSex()); updStudent.setDateOfBirth(student.getDateOfBirth()); updStudent.setGroupId(student.getGroupId()); updStudent.setEducationYear(student.getEducationYear()); } // Удалить студента public void deleteStudent(Student student) { // Надо найти нужного студента (по его ИД) и удалить Student delStudent = null; for (Student si : students) { if (si.getStudentId() == student.getStudentId()) { // Вот этот студент - запоминаем его и прекращаем цикл delStudent = si; break; } } students.remove(delStudent); } // Этот код позволяет нам изменить кодировку // Такое может произойти если используется IDE - например NetBeans. // Тогда вы получаете просто одни вопросы, что крайне неудобно читать public static void printString(Object s) { //System.out.println(s.toString()); try { System.out.println(new String(s.toString().getBytes("windows-1251"), "windows-1252")); } catch (UnsupportedEncodingException ex) { ex.printStackTrace(); } } public static void printString() { System.out.println(); } }
Итак, самое интересно на взгляд автора кроется в следующих функциях: loadGroups и loadStudent - эти функции на сегодня делают неблагодарную работу. Они создают данные и помещают их в коллекции. Остальные функции как раз и демонстрируют в какой-то мере возможности коллекций.
insertStudent - здесь вы можете видеть, что добавление в коллекцию -
очень простая опреация. Вы просто добавляете и все. Вам не надо думать о том, как ее расширять, где
взять памяти. Вы просто вызываете метод add. deleteStudent, updateStudent - эти функции демонстрируют некоторые сложности при работе с коллекциями. Во-первых, здесь есть демонстрация того, каким образом можно просмотреть все элементы коллекции. Как вы могли заметить, функции достаточно похожи. В случае удаления мы просто удаляем найденного студента. В случае изменения - заменяем поля студента на новые. getStudentsFromGroup - здесь мы получаем список студентов для определенной группы. Как видите, мы создаем еще один список, куда перемещаем всех студентов из конкретной группы и возвращаем уже вновь созданный список. moveStudentsToGroup - здесь все проще в связи с тем, что при просмотре коллекции мы получаем ссылки на реальные объекты и их изменение будет сразу заметно. removeStudentsFromGroup - здесь предложен следующий вариант - создать новый список из студентов, которые должны остаться.
Также надо обратить внимание на то, что наши списки групп и студентов - разные.
ArrayList - простой список, который имеет порядок в каком были
добавлены эдементы. Ну и конечно вам просто необходимо просмотреть код метода main (напоминаю еще раз). В нем мы сделали несколько вызовов реализованных методов и убедились, что они работают. Рекомендуем: Для более тонкого понимания как ищутся объекты в коллекциях рекомендуем вам посмотреть описания методов hashCode и equals. Коллекции часто рассматривают объекты как одинаковые, если у них одинаковый хэшкод. Мы привели не самый интересные решения. Если переопределить для класса Student методы hashCode и equals, которые будут считать студентов "равными" если у них одинаковый ИД, то процесс удаления будет короче - просто вызов students.remove(student)
ВНИМАНИЕ!!!
- /student
- /logic
- Student.java
- Group.java
- ManagementSystem.java
Для того, чтобы собрать наш проект Вам надо установить текующую директорию, в которой находится
директория students. Для сборки нашего проекта используется команда
javac students/logic/*.java Если вы собираетесь использовать пример из архива, то вам необходимо сделать вот такой вызов: javac -encoding UTF-8 students/logic/*.java
Дело в том, что я использую IDE NetBeans и в ней кодировка UTF-8, которая при обычном запуске
не определяется компилятором javac и вы получаете море ошибок.
Так что будьте внимательны. java -cp . students.logic.ManagementSystem
При запуске все данные записываются в файл out.txt.
Я ввел его из-за проблем с кодировкой. Файл всегда проще посмотреть - в том же
Notepad.
Если хочется все-таки выводить данные на консоль - закомментируйте кусок кода,
где я перенаправляю стандартный вывод в файл. И Вы можете просто не использовать русский шрифт
- используйте только английский. Тогда будет проще. Архив с исходными кодами: Исходный код |