#java #data-structures #singly-linked-list
#java #структуры данных #односвязный список
Вопрос:
Я реализую односвязный список на Java. Что мне не нравится в этом коде, так это то, что мне нужно проверять if (head.next == null)
каждый раз, когда я добавляю элемент. Но условие выполняется только один раз, при добавлении первого элемента.
Есть ли способ реализовать односвязный некруглый список без такого условия?
package sample;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class SinglyLinkedList<T> implements Iterable<T> {
private Node<T> head = new Node<T>(null);
private Node<T> last = null;
public SinglyLinkedList(T... elements) {
addAll(elements);
}
public void add(T element) {
if (head.next == null) {
head.next = new Node<T>(element);
last = head.next;
} else {
Node<T> newNode = new Node<T>(element);
last.next = newNode;
last = last.next;
}
}
public void addAll(T... elements) {
for (T element : elements) {
add(element);
}
}
@Override
public String toString() {
Iterator<T> iterator = iterator();
if (!iterator.hasNext()) {
return "[]";
}
StringBuilder builder = new StringBuilder();
builder.append("[");
while (iterator.hasNext()) {
T element = iterator.next();
builder.append(element);
if (!iterator.hasNext()) {
return builder.append("]").toString();
}
builder.append(", ");
}
return builder.toString();
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
Node<T> current = head;
@Override
public boolean hasNext() {
return current.next != null;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Node<T> temp = current;
current = current.next;
return temp.next.element;
}
};
}
private static class Node<T> {
private Node<T> next;
private T element;
Node(T element) {
this.element = element;
}
@Override
public String toString() {
return element.toString();
}
}
}
Комментарии:
1. Зачем вам нужен
last
объект?2. @MarounMaroun, потому что мне нужно добавить элементы в список. На самом деле, метод
add
должен быть вызванaddLast
3. @MarounMaroun, как вы думаете
last
, это излишне? Я видел несколько примеров, когда у них есть только указатель head, и перебирал список, чтобы добавить элемент в конец. Таким образом, добавление займет O (n), тогда как вставка в односвязный список должна занимать O (1). Смотрите также bigocheatsheet.com
Ответ №1:
Вы могли бы инициализировать last, чтобы указывать на head, а затем ваш if является избыточным:
private Node<T> head = new Node<T>(null);
private Node<T> last = head;
public void add(T element) {
Node<T> newNode = new Node<T>(element);
last.next = newNode;
last = last.next;
}
Комментарии:
1.Спасибо! Теперь я понимаю, почему это работает. So
last
содержит ссылку на ту же память,head
что и . Таким образом, при добавлении первого элементаlast.next = newNode;
это означает, чтоlast.next
, а такжеhead.next
будет содержать ссылку на первый добавленный элемент.
Ответ №2:
Есть много случаев, когда «хороший OO-дизайн» позволяет обходиться без проверок if / else; чаще всего с использованием некоторой формы полиморфизма.
Значение: вместо того, чтобы запрашивать у какого-либо объекта какое-либо свойство, чтобы затем принять решение об этом в вашем клиентском коде, вы каким-то образом убедитесь, что ваш клиентский код может просто вызвать метод для какого-либо другого объекта. И затем «if» «скрыт» в коде, который изначально сгенерировал этот «другой объект» и передал его вашему клиентскому коду. (в этих видео вы найдете несколько хороших примеров того, как это работает).
Но — я думаю, что в этом случае это было бы явным излишеством!
Дело в том, что с точки зрения удобства чтения эта одна проверка действительно не повредит (возможно, вы могли бы преобразовать вещи в большее количество методов). И производительность … также не имеет значения. Если ваш код вызывается так часто, что это имеет значение, JIT все равно сработает и, вероятно, создаст код, который в большинстве случаев напрямую использует правильную ветвь.
Таким образом: это хорошая реализация; и я думаю, вам не стоит беспокоиться об этом, если -проверьте там!