Skip to content

Commit 11667b6

Browse files
authored
Merge pull request #11 from pankaj-bind/fix-issue
Optimize CTQueue for O(1) Remove with Linked List (Fixes #9)
2 parents b748a33 + 3294c44 commit 11667b6

File tree

3 files changed

+229
-53
lines changed

3 files changed

+229
-53
lines changed

src/Containers-Queue-Tests/CTQueueTest.class.st

+139-18
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
"
22
I'm a simple queue i.e., first in first out structure.
33
I support basic collection protocol and in addition enqueue and dequeue as in Scala.
4-
5-
64
"
75
Class {
86
#name : #CTQueueTest,
@@ -25,6 +23,31 @@ CTQueueTest >> testAdd [
2523
self assert: (queue includes: 2)
2624
]
2725

26+
{ #category : #tests }
27+
CTQueueTest >> testAddAll [
28+
"Ensure queueAll adds multiple elements at once."
29+
| queue |
30+
queue := self queueClass new.
31+
queue addAll: #(10 20 30 40).
32+
self assert: queue remove equals: 10.
33+
self assert: queue remove equals: 20.
34+
self assert: queue remove equals: 30.
35+
self assert: queue remove equals: 40.
36+
]
37+
38+
{ #category : #tests }
39+
CTQueueTest >> testAddAllLargeCollection [
40+
"Test adding a large collection via addAll."
41+
| queue collection size |
42+
queue := self queueClass new.
43+
size := 50000.
44+
collection := (1 to: size) asArray.
45+
queue addAll: collection.
46+
self assert: queue size equals: size.
47+
1 to: size do: [ :i | self assert: queue remove equals: i ].
48+
self assert: queue isEmpty.
49+
]
50+
2851
{ #category : #tests }
2952
CTQueueTest >> testAddGarantyFIFOOrder [
3053
"Ensure elements are dequeued in FIFO order."
@@ -40,37 +63,66 @@ CTQueueTest >> testAddGarantyFIFOOrder [
4063
]
4164

4265
{ #category : #tests }
43-
CTQueueTest >> testEmptyQueue [
44-
self assert: self queueClass new isEmpty
66+
CTQueueTest >> testAddNilElement [
67+
"Test adding a nil element to the queue."
68+
| queue |
69+
queue := self queueClass new.
70+
queue add: nil.
71+
self assert: queue size equals: 1.
72+
self assert: queue peek isNil.
73+
self assert: queue remove isNil.
74+
self assert: queue isEmpty.
4575
]
4676

4777
{ #category : #tests }
48-
CTQueueTest >> testQueue [
49-
self assert: self queueClass new isEmpty
78+
CTQueueTest >> testDoIteration [
79+
"Test iterating over the queue with do: in FIFO order."
80+
| queue collected |
81+
queue := self queueClass new.
82+
queue addAll: #(10 20 30 40).
83+
collected := OrderedCollection new.
84+
queue do: [ :each | collected add: each ].
85+
self assert: collected asArray equals: #(10 20 30 40).
86+
self assert: queue size equals: 4. "Iteration should not modify the queue"
5087
]
5188

5289
{ #category : #tests }
53-
CTQueueTest >> testQueueGarantyFIFOOrder [
90+
CTQueueTest >> testEmptyQueue [
5491
self assert: self queueClass new isEmpty
5592
]
5693

5794
{ #category : #tests }
58-
CTQueueTest >> testAddAll [
59-
"Ensure queueAll adds multiple elements at once."
95+
CTQueueTest >> testEmptyQueueRemove [
6096
| queue |
6197
queue := self queueClass new.
62-
queue addAll: #(10 20 30 40).
63-
self assert: queue remove equals: 10.
64-
self assert: queue remove equals: 20.
65-
self assert: queue remove equals: 30.
66-
self assert: queue remove equals: 40.
98+
self assert: queue remove isNil.
6799
]
68100

69101
{ #category : #tests }
70-
CTQueueTest >> testEmptyQueueRemove [
102+
CTQueueTest >> testIncludes [
103+
"Test the includes: method for existing and non-existing elements."
71104
| queue |
72105
queue := self queueClass new.
73-
self assert: queue remove isNil.
106+
queue addAll: #(5 10 15).
107+
self assert: (queue includes: 10).
108+
self deny: (queue includes: 20).
109+
self assert: queue size equals: 3. "includes: should not modify the queue"
110+
]
111+
112+
{ #category : #tests }
113+
CTQueueTest >> testInterleavedAddRemove [
114+
"Test interleaved add and remove operations to ensure FIFO order and correctness."
115+
| queue |
116+
queue := self queueClass new.
117+
queue add: 1.
118+
queue add: 2.
119+
self assert: queue remove equals: 1.
120+
queue add: 3.
121+
self assert: queue remove equals: 2.
122+
queue add: 4.
123+
self assert: queue remove equals: 3.
124+
self assert: queue remove equals: 4.
125+
self assert: queue isEmpty.
74126
]
75127

76128
{ #category : #tests }
@@ -85,6 +137,31 @@ CTQueueTest >> testIsEmpty [
85137
self assert: queue isEmpty.
86138
]
87139

140+
{ #category : #tests }
141+
CTQueueTest >> testLargeQueuePerformance [
142+
"Verify FIFO behavior and performance with a large queue."
143+
| queue size startTime endTime duration maxDuration |
144+
queue := self queueClass new.
145+
size := 100000.
146+
147+
"Measure time to add elements"
148+
startTime := DateAndTime now.
149+
1 to: size do: [ :i | queue add: i ].
150+
endTime := DateAndTime now.
151+
duration := endTime - startTime.
152+
maxDuration := 5 seconds. "Expect adding 100,000 elements to take less than 5 seconds"
153+
self assert: duration < maxDuration description: 'Adding elements took too long'.
154+
155+
"Measure time to remove elements and verify FIFO order"
156+
startTime := DateAndTime now.
157+
1 to: size do: [ :i | self assert: queue remove equals: i ].
158+
endTime := DateAndTime now.
159+
duration := endTime - startTime.
160+
self assert: duration < maxDuration description: 'Removing elements took too long'.
161+
162+
self assert: queue isEmpty.
163+
]
164+
88165
{ #category : #tests }
89166
CTQueueTest >> testPeek [
90167
"Ensure peek returns the first element without removing it."
@@ -108,6 +185,16 @@ CTQueueTest >> testPoll [
108185
self assert: queue poll isNil.
109186
]
110187

188+
{ #category : #tests }
189+
CTQueueTest >> testQueue [
190+
self assert: self queueClass new isEmpty
191+
]
192+
193+
{ #category : #tests }
194+
CTQueueTest >> testQueueGarantyFIFOOrder [
195+
self assert: self queueClass new isEmpty
196+
]
197+
111198
{ #category : #tests }
112199
CTQueueTest >> testRemove [
113200
"Ensure remove behaves correctly, returning nil when empty."
@@ -121,10 +208,44 @@ CTQueueTest >> testRemove [
121208
]
122209

123210
{ #category : #tests }
124-
CTQueueTest >> testRemoveIfNone[
125-
"Ensure dequeueIfNone works correctly."
211+
CTQueueTest >> testRemoveIfNone [
212+
"Ensure removeIfNone works correctly."
126213
| queue result |
127214
queue := self queueClass new.
128215
result := queue removeIfNone: [ 'fallback' ].
129216
self assert: result equals: 'fallback'.
217+
]
218+
219+
{ #category : #tests }
220+
CTQueueTest >> testSizeAccuracy [
221+
"Test that the size method accurately reflects the number of elements."
222+
| queue |
223+
queue := self queueClass new.
224+
self assert: queue size equals: 0.
225+
queue add: 1.
226+
self assert: queue size equals: 1.
227+
queue addAll: #(2 3 4).
228+
self assert: queue size equals: 4.
229+
queue remove.
230+
self assert: queue size equals: 3.
231+
queue removeIfNone: [ nil ].
232+
self assert: queue size equals: 2.
233+
]
234+
235+
{ #category : #tests }
236+
CTQueueTest >> testStressAddRemove [
237+
"Stress test with many add and remove operations."
238+
| queue iterations |
239+
queue := self queueClass new.
240+
iterations := 10000.
241+
1 to: iterations do: [ :i |
242+
queue add: i.
243+
self assert: queue size equals: 1.
244+
self assert: queue remove equals: i.
245+
self assert: queue isEmpty.
246+
].
247+
"Add multiple elements and remove them"
248+
queue addAll: (1 to: 1000).
249+
1 to: 1000 do: [ :i | self assert: queue remove equals: i ].
250+
self assert: queue isEmpty.
130251
]

src/Containers-Queue/CTQueue.class.st

+61-35
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,111 @@
11
"
2-
I'm a simple FIFO queue i.e., first in first out structure. I support basic collection protocol and in addition enqueue and dequeue as in Scala.
3-
My basic support of collection API should be reviewd and probably improved (should check atomic queue protocol).
4-
5-
2+
I'm a simple FIFO queue i.e., first in first out structure. I support basic collection protocol with efficient O(1) add and remove operations using a singly linked list.
63
"
74
Class {
85
#name : #CTQueue,
96
#superclass : #Object,
107
#instVars : [
11-
'elements'
8+
'head',
9+
'tail',
10+
'size'
1211
],
1312
#category : #'Containers-Queue'
1413
}
1514

1615
{ #category : #adding }
1716
CTQueue >> add: anElement [
18-
"Add an element to the receiver. Note that the addition makes sure that when iterating over the receiver added first element are accessed first."
19-
20-
elements addLast: anElement.
17+
"Add an element to the end of the queue (FIFO order)."
18+
| newNode |
19+
newNode := CTQueueNode new value: anElement.
20+
head ifNil: [
21+
head := newNode.
22+
tail := newNode.
23+
] ifNotNil: [
24+
tail next: newNode.
25+
tail := newNode.
26+
].
27+
size := size + 1.
2128
^ anElement
2229
]
2330

2431
{ #category : #adding }
2532
CTQueue >> addAll: aCollection [
26-
"Add the elements contained in the argument to the receiver. Note that the addition makes sure that when iterating over the receiver added first element are accessed first."
27-
28-
elements addAllLast: aCollection.
33+
"Add all elements from aCollection to the end of the queue."
34+
aCollection do: [ :each | self add: each ].
2935
^ aCollection
3036
]
3137

3238
{ #category : #iterating }
3339
CTQueue >> do: aBlock [
34-
"iterates the elements of the receiver starting first by first added elements."
35-
36-
elements do: aBlock
40+
"Iterate over elements in FIFO order."
41+
| current |
42+
current := head.
43+
[ current notNil ] whileTrue: [
44+
aBlock value: current value.
45+
current := current next.
46+
]
3747
]
3848

3949
{ #category : #testing }
4050
CTQueue >> includes: anElement [
41-
42-
^ elements includes: anElement
51+
"Check if anElement exists in the queue."
52+
| current |
53+
current := head.
54+
[ current notNil ] whileTrue: [
55+
current value = anElement ifTrue: [ ^ true ].
56+
current := current next.
57+
].
58+
^ false
4359
]
4460

4561
{ #category : #initialization }
4662
CTQueue >> initialize [
63+
"Initialize an empty queue."
4764
super initialize.
48-
elements := OrderedCollection new.
65+
head := nil.
66+
tail := nil.
67+
size := 0.
4968
]
5069

5170
{ #category : #testing }
5271
CTQueue >> isEmpty [
53-
54-
^ elements isEmpty
72+
"Return true if the queue is empty."
73+
^ head isNil
5574
]
5675

57-
{ #category : #removing }
58-
CTQueue >> remove [
59-
"Return the older element of the receiver.."
60-
61-
^ elements ifEmpty: [ nil ] ifNotEmpty: [ elements removeFirst ].
76+
{ #category : #accessing }
77+
CTQueue >> peek [
78+
"Return the front element without removing it, or nil if empty."
79+
^ head ifNil: [ nil ] ifNotNil: [ head value ]
6280
]
6381

6482
{ #category : #removing }
65-
CTQueue >> removeIfNone: aBlock [
66-
"Return the older element of the receiver.."
67-
elements ifEmpty: [ ^ aBlock value ].
68-
^ elements removeFirst
83+
CTQueue >> poll [
84+
"Return and remove the front element, or nil if empty."
85+
^ self remove
6986
]
7087

7188
{ #category : #removing }
72-
CTQueue >> poll [
73-
"Returns and removes the front element, or nil if empty."
74-
^ elements ifEmpty: [ nil ] ifNotEmpty: [ elements removeFirst ].
89+
CTQueue >> remove [
90+
"Return and remove the oldest element, or nil if empty."
91+
| value |
92+
head ifNil: [ ^ nil ].
93+
value := head value.
94+
head := head next.
95+
head ifNil: [ tail := nil ].
96+
size := size - 1.
97+
^ value
7598
]
7699

77-
{ #category : #accessing }
78-
CTQueue >> peek [
79-
^ elements ifEmpty: [ nil ] ifNotEmpty: [ elements first ].
100+
{ #category : #removing }
101+
CTQueue >> removeIfNone: aBlock [
102+
"Return and remove the oldest element, or evaluate aBlock if empty."
103+
head ifNil: [ ^ aBlock value ].
104+
^ self remove
80105
]
81106

82107
{ #category : #accessing }
83108
CTQueue >> size [
84-
^ elements size
109+
"Return the number of elements in the queue."
110+
^ size
85111
]
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Class {
2+
#name : #CTQueueNode,
3+
#superclass : #Object,
4+
#instVars : [
5+
'value',
6+
'next'
7+
],
8+
#category : #'Containers-Queue'
9+
}
10+
11+
{ #category : #accessing }
12+
CTQueueNode >> next [
13+
^ next
14+
]
15+
16+
{ #category : #accessing }
17+
CTQueueNode >> next: aNode [
18+
next := aNode
19+
]
20+
21+
{ #category : #accessing }
22+
CTQueueNode >> value [
23+
^ value
24+
]
25+
26+
{ #category : #accessing }
27+
CTQueueNode >> value: anObject [
28+
value := anObject
29+
]

0 commit comments

Comments
 (0)