Ранее мы уже писали свой стек с помощью одномерного массива. Однако в этой статье мы напишем реализацию стека с помощью связанного списка и напишем для него несколько юнит-тестов.
Своя реализация стека на Java
С теорией можно ознакомиться в нашей предыдущей реализации стека.
«А зачем переизобретать колесо?» — спросите вы, ведь все уже написано до нас и нужно просто уметь использовать. Абсолютно согласен с такой позицией, но для более глубокого понимания работы стека и как он устроен, часто следует писать свою реализацию. В этой реализации стека мы воспользуемся связанным списком.
Лучшие практики написания стека
Для начала хотелось бы выделить основные правила и подходы, которым я буду следовать при написании стека на Java. Естественно, это касается не только реализации на Java, ведь подходы к реализации структуры данных должны быть универсальны. Однако некоторые моменты будут в контексте Java-реализации.
Операции push, pop за постоянное время
Временная сложность для операций вставки (
push
) и получения (pop
) должны быть O(1)
. Не имеет значения сколько элементов в стеке у нас будет, эти операции должны проводиться за постоянное время. Например, если мы вызываем операцию pop
, то она должна вернуть последний добавленный элемент — O(1)
.Избегаем печати в консоль
Методы, которые отвечают за отображение элементов списка также должны быть тестируемые. А тестирование метода, который просто печатает что-то в консоль, не совсем правильно. Вместо этого в Java-реализации можно переопределить метод
toString()
и работать с ним.Универсальный стек
Вместо того, чтобы завязываться под определенный тип данных, можно сделать наш стек универсальным с использованием дженериков (родовых типов). И в качестве типа входных элементов стека использовать
T
.Изобретение колеса
Когда мы пишем свою реализацию какой-то устоявшейся структуры данных, алгоритмов или шаблонов, желательно наследовать существующий подход (будь-то способ именования методов, обработки ошибок и т.д.). Для этого в Java хорошим примером является известный нам класс
java.util.LinkedList
.Пишем стек с использованием связанного списка на Java
В реализации ниже используется параметризированный класс LinkedListStack, который использует внутри себя связанный список для работы с данными:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
public class LinkedListStack<T> {
private final LinkedList<T> linkedList = new LinkedList<>();
public void push(T data) {
linkedList.addFirst(data);
}
public T pop() {
return linkedList.removeFirst();
}
public boolean isEmpty() {
return linkedList.isEmpty();
}
@Override
public String toString() {
return linkedList.toString();
}
}
class LinkedList<T> {
// внутренний класс, который представляет элемент списка
private static class Node<T> {
// данные
private T data;
// указатель на следующий элемент
private Node<T> next;
public Node(T data) {
this.data = data;
}
@Override
public String toString() {
return data.toString();
}
}
private Node<T> first = null;
// используется для push операции
public void addFirst(T data) {
Node<T> newFirst = new Node<T>(data);
newFirst.next = first;
first = newFirst;
}
// используется для pop операции
public T removeFirst() {
Node<T> oldFirst = first;
first = first.next;
return oldFirst.data;
}
@Override
public String toString() {
StringBuilder listBuilder = new StringBuilder();
Node currentNode = first;
while (currentNode != null) {
listBuilder.append(currentNode).append(",");
currentNode = currentNode.next;
}
return listBuilder.toString();
}
public boolean isEmpty() {
return first == null;
}
}
|
Из реализации видно, что у нас всего 3 класса:
LinkedList<T>
— реализация связанного спискаNode<T>
— внутренний класс, который представляет собой элемент спискаLinkedListStack<T>
— класс с реализацией базовых операций стека. Внутри себя использует LinkedList<T> для манипуляции данными.
Теперь напишем несколько юнит-тестов для проверки операций в нашей реализации стека. Используем стандартный JUnit:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class TestLinkedListStack {
List<Integer> testData;
@Before
public void init() {
testData = Arrays.asList(600, 1200, 1500, 2100);
}
@Test
public void testPushPop() {
LinkedListStack<Integer> stack = new LinkedListStack<>();
stack.push(1200);
stack.push(1500);
assertEquals("1500,1200,", stack.toString());
assertEquals(1500, (int) stack.pop());
assertEquals("1200,", stack.toString());
}
@Test
public void testPopIsEmpty() {
LinkedListStack<Integer> stack = new LinkedListStack<>();
assertTrue(stack.isEmpty());
for (Integer value : testData) {
stack.push(value);
}
assertFalse(stack.isEmpty());
for (int i = testData.size(); i > 0; --i) {
assertEquals(testData.get(i - 1), stack.pop());
}
}
@Test
public void testPushIsEmpty() {
LinkedListStack<Integer> stack = new LinkedListStack<>();
assertTrue(stack.isEmpty());
for (Integer value : testData) {
stack.push(value);
}
assertFalse(stack.isEmpty());
}
}
|
Все наши тесты проходят, реализация стека на Java — успешная.
Следите за обновлениями и учите структуры данных вместе с Javadevblog.com.
Смотри также:
- Зачем нужна Java. http://fetisovvs.blogspot.com/2014/07/java.html
- Концепции объектно-ориентированного программирования — ООП в Java. http://fetisovvs.blogspot.com/2017/01/java-java.html
- Java-ресурсы, на которые есть смысл подписаться. http://fetisovvs.blogspot.com/2016/09/java-java.html
- Подборка популярных ошибок начинающих Java программистов. http://fetisovvs.blogspot.com/2016/10/java-java_29.html
- Двести пятьдесят русскоязычных обучающих видео докладов и лекций о Java. http://fetisovvs.blogspot.com/2015/12/java-5-java-java.html
- Абстрактные классы и методы. http://fetisovvs.blogspot.com/2017/02/java.html
- Полное руководство по Java Reflection API. Рефлексия на примерах. http://fetisovvs.blogspot.com/2017/02/java-reflection-api-java.html
- ТОП-3 способа конвертировать массив в ArrayList. Пример на Java. http://fetisovvs.blogspot.com/2016/09/3-arraylist-java-java.html
- Integer и int. http://fetisovvs.blogspot.com/2016/07/integer-int-java.html
- Ввод–вывод в Java. http://fetisovvs.blogspot.com/2016/05/java-java_28.html
- Enum-Всемогущий. http://fetisovvs.blogspot.com/2017/02/enum-java.html
- Популярные методы для работы с Java массивами. http://fetisovvs.blogspot.com/2016/09/java-java_29.html
- В чем разница между Set и Set. Пример использования Set. http://fetisovvs.blogspot.com/2016/09/set-set-set-java.html
- Пример использования метода replace в Java. Как заменить символ в строке? http://fetisovvs.blogspot.com/2017/01/replace-java-java.html
- Класс Scanner в Java — описание и пример использования. http://fetisovvs.blogspot.com/2017/01/scanner-java-java.html
- Пример использования метода trim в Java: как удалить пробелы в начале и конце строки? http://fetisovvs.blogspot.com/2017/01/trim-java-java.html
- Чтение и запись CSV файла с помощью SuperCSV. http://fetisovvs.blogspot.com/2017/01/csv-supercsv-java-java.html
- Конструкция try/catch/finally (исключения). http://fetisovvs.blogspot.com/2017/01/trycatchfinally-java.html
- Шпаргалка Java программиста 7.1 Типовые задачи: Оптимальный путь преобразования InputStream в строку. http://fetisovvs.blogspot.com/2016/04/java-71-inputstream-java.html
- Шпаргалка Java программиста 8. Библиотеки для работы с Json (Gson, Fastjson, LoganSquare, Jackson, JsonPath и другие). http://fetisovvs.blogspot.com/2016/04/java-8-json-gson-fastjson-logansquare.html
- Реализация ООП-наследования в классах, работающих с SQL и MS Entity Framework. http://fetisovvs.blogspot.com/2017/02/sql-ms-entity-framework.html
- Как установить соединение с СУБД MySQL в IntelliJ IDEA в редакции Community. http://fetisovvs.blogspot.com/2016/04/mysql-intellij-idea-community-java.html
- Работа с Bluetooth LE из Java-приложений. http://fetisovvs.blogspot.com/2016/07/bluetooth-le-java-java.html
- Программируем… выход из лабиринта. http://fetisovvs.blogspot.com/2015/10/java.html
- Основы работы с IntelliJ IDEA. Интерфейс программы. http://fetisovvs.blogspot.com/2016/09/intellij-idea-java.html
- Как с помощью maven работать с библиотеками, которых в maven нет. http://fetisovvs.blogspot.com/2017/03/maven-maven-java.html
Комментариев нет:
Отправить комментарий