ListIter   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 56
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 30
c 1
b 0
f 0
dl 0
loc 56
ccs 23
cts 23
cp 1
rs 10
wmc 11

3 Methods

Rating   Name   Duplication   Size   Complexity  
B remove() 0 26 6
A hasNext() 0 8 2
A next() 0 14 3
1
/*
2
 * This file is part of Araknemu.
3
 *
4
 * Araknemu is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * Araknemu is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with Araknemu.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2022 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.util;
21
22
import org.checkerframework.checker.nullness.qual.Nullable;
23
24
import java.util.Iterator;
25
import java.util.NoSuchElementException;
26
27
/**
28
 * Simple implementation of linked list with safe add and remove operations
29
 *
30
 * - Adding an element while iterating will be added to the end, and taken in account by the iterator (i.e. will iterator on it)
31
 * - Removing an element can only be performed during iteration, and will not break other iterators
32
 *
33
 * This code is allowed :
34
 * <pre>{@code
35
 * SafeLinkedList<MyObject> list = new SafeLinkedList<>();
36
 * // Add some elements...
37
 *
38
 * Iterator<MyObject> it = list.iterator();
39
 *
40
 * while (it.hasNext()) {
41
 *     MyObject o = it.next();
42
 *
43
 *     if (o.shouldBeDeleted()) {
44
 *         it.remove();
45
 *     }
46
 *
47
 *     if (o.hasChild()) {
48
 *         list.add(o.getChild());
49
 *     }
50
 * }
51
 * }</pre>
52
 *
53
 * @param <E> Stored element type
54
 */
55 1
public final class SafeLinkedList<E> implements Iterable<E> {
56 1
    private @Nullable Node<E> first = null;
57 1
    private @Nullable Node<E> last = null;
58
59
    /**
60
     * Add an element to the end of the list
61
     *
62
     * Unlike default {@link java.util.LinkedList}, calling this method will not raise an
63
     * {@link java.util.ConcurrentModificationException} while iterating.
64
     *
65
     * @param element Element to add
66
     */
67
    public void add(E element) {
68 1
        final Node<E> newNode = new Node<>(element);
69
70 1
        if (last != null) {
71 1
            last.next = newNode;
72 1
            last = newNode;
73 1
            return;
74
        }
75
76 1
        first = last = newNode;
77 1
    }
78
79
    @Override
80
    public Iterator<E> iterator() {
81 1
        return new ListIter();
82
    }
83
84
    private static class Node<E> {
85
        private final E element;
86 1
        private @Nullable Node<E> next = null;
87
88 1
        public Node(E element) {
89 1
            this.element = element;
90 1
        }
91
    }
92
93 1
    private class ListIter implements Iterator<E> {
94 1
        private SafeLinkedList.@Nullable Node<E> current = null;
95 1
        private SafeLinkedList.@Nullable Node<E> previous = null;
96
97
        @Override
98
        public boolean hasNext() {
99
            // First loop
100 1
            if (current == null) {
101 1
                return first != null;
102
            }
103
104 1
            return current.next != null;
105
        }
106
107
        @Override
108
        public E next() {
109 1
            if (current == null) {
110 1
                current = first;
111
            } else {
112 1
                previous = current;
113 1
                current = current.next;
114
            }
115
116 1
            if (current == null) {
117 1
                throw new NoSuchElementException();
118
            }
119
120 1
            return current.element;
121
        }
122
123
        @Override
124
        public void remove() {
125 1
            final Node<E> current = this.current;
0 ignored issues
show
Comprehensibility introduced by
The variable currentshadows a field with the same name declared in line 94. Consider renaming it.
Loading history...
126
127
            // next not yet called, or remove already called (current set to previous)
128 1
            if (current == null || previous == current) {
129 1
                throw new IllegalStateException();
130
            }
131
132
            // Remove the first element
133 1
            if (current == first) {
134 1
                first = current.next;
135
            }
136
137
            // Remove the last element
138 1
            if (current == last) {
139 1
                last = previous;
140
            }
141
142
            // Detach current node
143 1
            if (previous != null) {
144 1
                previous.next = current.next;
145
            }
146
147
            // Change "current" node
148 1
            this.current = previous;
149 1
        }
150
    }
151
}
152