Jump to content

Generic Queue with memory references

- - - - -

  • Please log in to reply
7 replies to this topic

#1
JackPanzer

JackPanzer

    Newbie

  • Members
  • Pip
  • 4 posts
Hello everyone!

I think this is a continuation of this other post:

http://forum.codecall.net/java-tutorials/41122-generic-queue.html

In wich I read about a generic queue made with memory references. So now that I have a little free time I'm going to post my code and explain it.
This one could be a little complicated to understand because I am using what we can call "pointers". For that I've implemented a generic Node, wich has a pointer to the next element of the queue and for the element that is before him.

package classes;


public class Node<T>

{

	private T element;

	private Node<T> next;

	private Node<T> before;

	

	public Node(T element)

	{

		this.element = element;

		next = before = null;

	}

	

	public void setElement(T element)

	{

		this.element = element;

	}

	public T getElement()

	{

		return element;

	}

	

	public void setNext(Node<T> next)

	{

		this.next = next;

	}

	public Node<T> getNext()

	{

		return next;

	}

	

	public void setBefore(Node<T> before)

	{

		this.before = before;

	}

	public Node<T> getBefore()

	{

		return before;

	}

}

Quite simple, a generic element and two references, that's it.

The generic element is quite simple but now is when we have problems. It is you who is supposed to check if every memory reference has been asigned correctly. Here is the class:

package classes;


public class GenericQueue<T>

{

	private Node<T> first;

	private Node<T> last;

	private int numElem; //Number of elements

	

	public GenericQueue()

	{

		numElem = 0;

		first = last = null;

	}

	

	public GenericQueue(T value)

	{

		numElem = 1;

		first = new Node<T>(value);

		last = first;

	}

	

	public int getNumElem()

	{

		return numElem;

	}

	

	public T getHeadElement()

	{		

		return first.getElement();

	}


	public void Push(T value)

	{

		switch(numElem)

		{

		case 0:

			first = new Node<T>(value);

			last = first;

			break;

		case 1:

			last = new Node<T>(value);

			last.setBefore(first);

			first.setNext(last);

			break;

		default:

			Node<T> aux = new Node<T>(value);

			aux.setBefore(last);

			last.setNext(aux);

			last = aux;

			break;

		}

		numElem++;

	}

	

		public T Pop()

	{

		T value = null;

		if(numElem != 0)

		{

			if(numElem == 1)

			{

				value = last.getElement();

				first = last = null;

			}

			else

			{

				value = first.getElement();

				first = first.getNext();

				first.setBefore(null);

			}

			numElem--;

		}

		return value;

	}

	

	public boolean isEmpty()

	{

		return numElem == 0;

	}

}

Instead of an array or arraylist, we have two main variables in the class: the head (first) and the tail (last) of the queue. The numElem is just the number of element in the queue. I've made it like this just to don't have to calculate it constantly, and only has a public get, no setters.

isEmpty: returns if the queue is empty by looking the number of element in the queue

getHeadElement: returns the first element in the queue. Does the same thing as Pop but without deleting the first element of the queue

getNumElem: returns the number of elements in the queue

GenericQueue(): Constructor. Makes an empty queue

GenericQueue(T value): Constructor. Makes a queue with one element

And now, the main methods: push and pop.

PUSH
We have 3 possible scenarios when we insert an element in a queue:
1.- The queue is empty
2.- The queue has one element
3.- The queue has more than one element

For the first possibility, we just have to do the same as the constructor GenericQueue(T value), no big deal.

For the second, we have to create a node. That will be our new last element of the queue (the tail), now we have to assign the element before the tail as the head node, and the next node of the head as the tail node

last = new Node<T>(value);

last.setBefore(first);

first.setNext(last);

For the last one, we create an auxiliar node and set the same reference as the tail. After that create the new tail node and assign references as in the second option

Node<T> aux = new Node<T>(value);

aux.setBefore(last);

last.setNext(aux);

last = aux;

POP
In this method we have another two options:
1.- The queue has 1 element
2.- The queue has more than 1 element

For the first option we save the value of the head or the tail (it will have the same references) and then, delete the node.

value = last.getElement();

first = last = null;

For the last option we save the vaule of the head. After this reassign the reference of the head to his next element

value = first.getElement();

first = first.getNext();

first.setBefore(null);

An here we have a main class to see how it works x)

package main;


import classes.GenericQueue;


public class Main {


	/**

	 * @param args

	 */

	public static void main(String[] args)

	{

		// TODO Auto-generated method stub

		GenericQueue<Integer> q = new GenericQueue<Integer>();

		q.Push(3);

		q.Push(7);

		q.Push(8);

		System.out.println("First element in the queue: "+q.getHeadElement());

		while(!q.isEmpty())

		{

			System.out.println(q.Pop());

		}

	}


}

And it's output:

First element in the queue: 3
3
7
8

Hope this could help someone x)

#2
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,118 posts
  • Location:Vancouver, Eh! Cleverness: 200
Thank you for this submission, JackPanzer!
Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.

#3
wim DC

wim DC

    Writes binary right handed and hex left handed

  • Members
  • PipPipPipPipPipPipPipPipPip
  • 2,084 posts
  • Programming Language:Java, JavaScript, PL/SQL
  • Learning:Java
You should consider implementing the Queue and/or List interface, then the class becomes much more usable with Java's other classes.

#4
scottbomb

scottbomb

    Newbie

  • Members
  • PipPip
  • 21 posts
I'm stuck on one thing: how do I prevent privacy leaks with generics?

The code above defines element, next, and before as being private objects. However, the getters simply return the memory address of the same objects. Therefore, this defeats their privacy, right?

One thing my professors drilled into my head was to make copy methods to ensure that I never returned a mutable object from a getter but rather a COPY of the mutable object. But how do I make a copy() method with generics? After all, the whole purpose of using generics is the allow the programmer to use whatever object they want and the code has no way to guarantee the object being copied even has a copy() method (I'm sure the compiler would complain!). I might use clone() (because all objects have clone) but how exactly would I call it? Like this?

Object myObject = new Object(T.clone());

Something about that just doesn't look right. Even still, I don't like the clone method (I never use it, hence my hesitation here) because it only make shallow copies.

Perhaps there IS no answer? Are generics, then, only good for immutable objects such as String and primitives?

#5
wim DC

wim DC

    Writes binary right handed and hex left handed

  • Members
  • PipPipPipPipPipPipPipPipPip
  • 2,084 posts
  • Programming Language:Java, JavaScript, PL/SQL
  • Learning:Java
- You can't return a copy of the thing in the list because.... you can't make a copy:P
Since clone() won't work unless object T has the cloneable interface (or the class can override clone() in a bad way). And limiting the List class or Queue to only Cloneable objects is stupid.

- For Queues and collections in general I rather prefer to have the original Object. Saves me lines to overwrite the object in the collection every time I DO want to change the object in the collection.

#6
scottbomb

scottbomb

    Newbie

  • Members
  • PipPip
  • 21 posts
Cool, thanks. Glad to see I'm not losing it.

#7
scottbomb

scottbomb

    Newbie

  • Members
  • PipPip
  • 21 posts
One thing still bothers me about this code. In the Node class, the instance variables next and before are private, as are the variables first and last in the GenericQueue class. Since the get and set methods for these are passing the actual mutable objects instead of a copy (not a safe practice, but I digress...) why bother making them private when the privacy is broken anyway by the methods? I'm seeing this in Java programming textbooks as well.

Edited by scottbomb, 13 February 2012 - 08:54 AM.


#8
wim DC

wim DC

    Writes binary right handed and hex left handed

  • Members
  • PipPipPipPipPipPipPipPipPip
  • 2,084 posts
  • Programming Language:Java, JavaScript, PL/SQL
  • Learning:Java

Quote

not a safe practice
Cause it's not unsafe :P




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users