Intro to the
circular buffer
or
circular array
- Circular
buffer/array:
-
Circular buffer
= a data structure
that uses an
array
as if it were
connected
"end-to-end"
|
Schematically:
- This data structure is
also known as:
-
Circular array
of
Ring buffer
|
|
The circular buffer
data structure
- The circular buffer
data structure consists of
an array with
2 "pointers" or indices:
-
Read
pointer = the
index of the
read position in array
-
Write
pointer = the
index of the
write position in array
|
Schematically:
|
Wrap-around nature of the
circular buffer
- When the write
(or read)
pointer
reaches the
end
of the array/buffer...
write(99):
|
Wrap-around nature of the
circular buffer
- The write
(or read)
pointer
will
wrap around
and
reset to
0:
write(99):
|
Implementing the
wrap-around nature of the
circular buffer
- The
achieve the
wrap araound effect,
the read
(or write)
pointer (=index) variable
is updated
using
"modulo arithmetic":
Normal increment operation:
write = write + 1;
(read = read + 1; // for read operations)
Increment operation with modulo arithmetic:
write = (write + 1) % buf.length;
|
- Example:
Suppose: write = 15
write = (write + 1) % buf.length; // buf.length = 16
= (15 + 1) % 16
= 16 % 16
= 0
|
|
The write operation on a
circular buffer
The basic implementation
of the write operation on a
circular buffer
- The
basic
implementation of the
write( ) operation
(without checking
if buffer is
full):
void write(T e) // T is the type of the data stored in the queue
{
buf[write] = e;
write = (write+1)%buf.length; // Use modulo arithmetic !
}
|
|
The read operation on a
circular buffer
The
basic implementation
of the read operation on a
circular buffer
- The
basic
implementation of the
read( )
operation
(without checking
if buffer is
empty
):
T read( ) // T is the type of the data stored in the queue
{
T retVal = buf[read];
read = (read+1)%buf.length; // Use modulo arithmetic !
return retVal;
}
|
|
How to tell if a
circular buffer if
empty
- Consider the
following
circular buffer with
2 items:
- The circular buffer is
not
empty and:
|
How to tell if a
circular buffer if
empty
- After
reading
2 elements
from
the circular buffer:
- The circular buffer is
empty and:
|
Now.... how to tell if a
circular buffer if
full
- Consider the
following
circular buffer with
2 empty spots:
- The circular buffer is
not
full and:
|
Now.... how to tell if a
circular buffer if
full
- After
writing
1 element
(13)
to
the circular buffer:
- The circular buffer is
not
full yet and:
|
How to tell if a
circular buffer if
empty
- After
writing another
element
from
the circular buffer:
- The circular buffer is
full and:
read == write // Problem: this also mean "empty"
|
|
Breaking the ambiguity
- The following trick is
used to
break the
ambiguity:
- A circular buffer is
full when
the is 1 (= one) empty slot
left
|
Schematically:
|
I can now show you
the
implementation of
an Integer queue
using a
circular buffer
Recall:
the Queue interface
definition
- Sample
Queue interface
definition:
interface MyQueueInterface<E>
{
boolean isEmpty(); // returns true is queue is empty
boolean isFull(); // returns true is queue is full
void enqueue(E e); // Insert elem e at the back of the queue
E dequeue(); // Remove the elem at the front
// of the queue and return it
E peek(); // Return the elem at the front
// without removing it
}
|
|
A class that
defines the
Queue interface
must
override all these
methods....
Variable definitions and the
constructor of the
IntegerQueue class
- We implement the
IntegerQueue
using a
circular buffer:
public class IntegerQueue implements MyQueueInterface<Integer>
{
private Integer[] buf; // Array of the circular buffer
private int read; // read pointer (= head index of queue)
private int write; // write pointer (= tail index of queue)
// Constructor
public IntegerQueue(int N)
{
// Variable of the circular buffer
buf = new Integer[N]; // Create array
read = 0; // Initialize read pointer
write = 0; // Initialize write pointer
}
// Interface methods will be discussed next...
boolean isEmpty() { ... }
boolean isFull() { ... }
void enqueue(Integer e) { ... }
Integer dequeue() {... }
Integer peek() { ... }
}
|
|
The implementation of
the isEmpty( ) and
isFull( )
methods
- The
queue
(or circular buffer)
is
empty when
read pointer
== write pointer:
public boolean isEmpty() // returns true is queue is empty
{
return read == write;
}
|
- The
queue
(or circular buffer)
is
full when
there is
one (= 1) open spot left:
public boolean isEmpty() // returns true is queue is full
{
/* -------------------------------------------------
buffer has 1 open spot
<==> write 1 item into the buffer and it's full
------------------------------------------------- */
return (write+1)%buf.length == read;
}
|
|
The implementation of
the isEmpty( ) and
isFull( )
methods
- The enqueue( ) method:
public void enqueue(Integer e)
{
if ( isFull () )
{
System.out.println("Full"); // Or throw exception
return ;
}
buf[ write ] = e;
write = (write+1)%buf.length; // Use modulo arithmetic
}
|
- The dequeue( ) method:
public Integer dequeue()
{
if ( isEmpty() )
{
System.out.println("Empty"); // Or throw exception
return null;
}
Integer retVal = buf[ read ];
read = (read+1)%buf.length; // Use modulo arithmetic
return retVal;
}
|
|
The implementation of
the peek( )
method
- The peek( ) method:
public Integer peek()
{
if ( isEmpty() )
{
System.out.println("Empty"); // Or throw exception
return null;
}
else
{
return buf[ read ];
}
}
|
|
DEMO:
10-queue/01-circular-buf/Demo.java +
IntegerQueue.java
❮
❯