2

I am trying to implement a simple HashTable in Java that uses a Linked List for collision resolution, which is pretty easy to do in C, but I don't know how to do it in Java, as you can't use pointers...

First, I know that those structures are already implemented in Java, I'm not planning on using it, just training here...

So I created an element, which is a string and a pointer to the next Element:

public class Element{
        private String s;
        private Element next;

        public Element(String s){
            this.s = s;
            this.next = null;
        }

        public void setNext(Element e){
            this.next = e;
        }

        public String getString(){
            return this.s;
        }

        public Element getNext(){
            return this.next;
        }

        @Override
        public String toString() {
            return "[" + s + "] => ";
        }
    }

Of course, my HashTable has an array of Element to stock the data:

public class CustomHashTable {
    private Element[] data;

Here is my problem:

For example I want to implement a method that adds an element AT THE END of the linked List (I know it would have been simpler and more efficient to insert the element at the beginning of the list, but again, this is only for training purposes). How do I do that without pointer?

Here is my code (which could work if e was a pointer...):

public void add(String s){
        int index = hash(s) % data.length;
        System.out.println("Adding at index: " + index);
        Element e = this.data[index];
        while(e != null){
            e = e.getNext();
        }
        e = new Element(s);
    }

Thanks!

nbarraille
  • 9,926
  • 14
  • 65
  • 92
  • 1
    Have you actually tried running your example code? Because for all intents, Java references are the same as C pointers. – Anon Jan 12 '11 at 15:42
  • And once you've got this down, you can dabble in generics! – dkarp Jan 12 '11 at 15:42
  • @Anon: They are for this example, but not for all. – Joey Jan 12 '11 at 15:43
  • Except that your example `add()` method will fail because you're not keeping track of the tail of the list, and not assigning the new `Element` to the tail ... – Anon Jan 12 '11 at 15:43
  • 1
    @Joey - the places where they're not equivalent are (1) array names are not pointers, (2) you can't cast a pointer to/from an integer, and (3) you can't cast a pointer to an arbitrary type. You don't need any of those features to implement a linked list, or for that matter, most real-world applications. And Java provides work-arounds when you do need those features. – Anon Jan 12 '11 at 15:45
  • I suggest you look at the code for the built in HashMap which comes with the JDK or is online http://www.docjar.com/html/api/java/util/HashMap.java.html or just use the builtin HashMap – Peter Lawrey Jan 12 '11 at 18:18

7 Answers7

5
public void add(String s){
    int index = hash(s) % data.length;
    System.out.println("Adding at index: " + index);
    Element curr = new Element(s);
    Element e = this.data[index];
    if (e == null) {
       this.data[index] = curr;
       return;
    }
    while(e.getNext() != null){
        e = e.getNext();
    }
    e.setNext(curr);
}
Joel
  • 29,538
  • 35
  • 110
  • 138
1

If you want to write your own, start by having it implement the java.util.List interface.

If you can use a library class, use the java.util.LinkedList.

Now it's been pointed out to me that you're "practicing", so some good advice might be off limits according to some, but you should be aware of generics. I'd recommend reading up on them and building them into your Element (Node?) and List implementations. Generics were born for use with collections in just this way. I'd start here:

package list;

public class Node<T>
{
    private T value;
    private Node<T> prev;
    private Node<T> next;
    // You add the rest.
}
duffymo
  • 305,152
  • 44
  • 369
  • 561
  • 1
    If he's just practicing for the moment, I guess it's too early to bring in interfaces and Java's collection framework. – Joey Jan 12 '11 at 15:41
  • I don't think it's too early to bring in the interface. If nothing else, it'll clarify what being a "List" means. – duffymo Jan 12 '11 at 15:42
  • That's what I would do, but the point here is I want to be able to implement it using only Arrays, so I can see all the mechanisms. – nbarraille Jan 12 '11 at 15:44
  • Implement with arrays doesn't exclude List interface. How you implement is your affair; hide the implementation behind the List interface and you can do what you please, as long as you conform to the contract that the List interface provides for clients. – duffymo Jan 12 '11 at 15:45
  • 2
    -1 I voted this down as he says in the question he is just practicing. I also think that there is no harm in not using the `java.util.List` interface at this point. It would be good practice to in my opinion to refactor this laster to use the java `List` interface. And you also didn't bother to actually answer his question at all. – Casey Jan 12 '11 at 15:48
  • Funny coming from you, Casey, as you offered nothing at all. Where's yours? Just because he's practicing doesn't mean that he shouldn't use the interface. It makes the contract for a List clear; I had no idea whether he would be able to articulate it properly based on his original post. I thought it'd be helpful to point it out. I don't always have time to post code or give an answer all the time I would like, so I might toss off something rather than say nothing. Your down vote is your business, but your reasons are flawed, in my opinion. – duffymo Jan 12 '11 at 15:53
  • IMHO, this should have been a comment, because it is not an answer to the question `How do I do that without pointer?`. – Ishtar Jan 12 '11 at 15:56
  • @duffymo There were other good answers here already. I didn't feel the need to have to provide my own answer to decide that yours wasn't very good. I would rather vote one of the others up that actually answered the question. I see you have added more to your question. Thanks. – Casey Jan 12 '11 at 15:59
  • Those answers came after mine; it was first up, so "better" was decided after the fact. I don't mind you voting others up or mine down; I think your reasoning was flawed. But it's no worry; you're entitled. Thank you, Casey. – duffymo Jan 12 '11 at 16:01
  • Well first up is not better. Your original answer did not answer his question. – Casey Jan 12 '11 at 16:02
  • Well, it exemplifies one problem I have with learning Java as a first language. You can upgrade relatively safely from a structured language to an OO one, keeping most of the concepts and just add some new knowledge. In this case the OP just tries figuring out how to implement a linked list which is fundamentally a problem way below OOP. You can safely add stuff like encapsulation (ok, forced here, due to classes already) and generics later without harm. But if you start out with an OO language you need to explain those concepts quite early which muddles up the actual implementation of the ADT – Joey Jan 12 '11 at 16:11
  • Joey I agree. However, you can only take in so much at a time. – Casey Jan 12 '11 at 16:12
1

For your purposes here Java's references are not used differently from C's pointers. In fact, the major problem (or benefit, depending on who you ask) pointers have in C is not that they can point to something, but that you can do math with them.

For just the pointing purposes, you can do the same here:

e.setNext(new Element(s));

instead of

e = new Element(s);

(which would just let the variable e point to a new element, but won't change anything on the old ones).

and you're done.

Joey
  • 344,408
  • 85
  • 689
  • 683
0

Reassigning a local variable at the end of a method isn't going to change anything. You need something like:

Element e = this.data[index];
while (e.getNext() != null) {
    e = e.getNext();
}

Then e refers to the element at the end of the list. Create a new element, and set that as the next one:

Element newElement = new Element(s);
e.setNext(newElement);

For efficiency, I'd urge you to consider doubly-linked nodes and a list type which knows about the head and tail of the list. Differentiate between the list as a whole (which knows the head, the tail, and probably the count), and a node within the list (which knows about the next, previous, and possibly what list it belongs to).

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I think a singly-linked list can be justified in this particular case. OP is making a hashtable, so the lists should contain only a few nodes. The slight efficiency gain might not outweigh the extra memory consumption. – Ishtar Jan 12 '11 at 16:34
  • @Ishtar: Possibly, yes. In fact, he could still maintain a list with a link to head and tail, but still make each node forward-only. – Jon Skeet Jan 12 '11 at 17:06
0

A recursive solution:

public class Element{
  /* ... */
  public void addTail(Element e){
    if (next == null)
      next = e; //we are at the end, add it
    else
      next.addTail(e);//let the next node take responsibility
  }
}

public void add(String s){

  int index = hash(s) % data.length;
  System.out.println("Adding at index: " + index);
  Element e = this.data[index];
  e.addTail(new Element(s));
}
Ishtar
  • 11,542
  • 1
  • 25
  • 31
0

LinkedList in java works as like that:

There is a static class like this:

private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous;
Entry(E element, Entry<E> next, Entry<E> previous) {
    this.element = element;
    this.next = next;
    this.previous = previous;
}
}

LinkedList constructors:

public LinkedList() {
    header.next = header.previous = header;
}

public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}

addAll method:

public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}

You should check here:

private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;

private Entry<E> addBefore(E e, Entry<E> entry) {
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}

private E remove(Entry<E> e) {
if (e == header)
    throw new NoSuchElementException();

    E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
    e.next = e.previous = null;
    e.element = null;
size--;
modCount++;
    return result;
}

private Entry<E> addBefore(E e, Entry<E> entry) {
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}

public boolean add(E e) {
addBefore(e, header);
    return true;
}
kamaci
  • 72,915
  • 69
  • 228
  • 366
0
//Single linked list implementation

public class Nodes {

    int data;
    Nodes next = null;

    public Nodes(int data) {
        this.data = data;
    }
}


public class Lists {

    static Nodes first = null;

    public static void insertBegin(int data) {
        Nodes temp = new Nodes(data);
        if(first == null) {
            first = temp;
        }
        else {
            temp.next = first;
            first = temp;           
        }
    }

    public static void insertEnd(int data) {
        Nodes temp = new Nodes(data);
        if(first == null) {
            first = temp;
        }
        else{
            Nodes n = first;
            while(n.next != null) {
                n = n.next;
            }
            n.next = temp;
        }
    }

    public static void insertPos(int pos, int data) {
        Nodes temp = new Nodes(data);
        if(first == null) {
            System.out.println("List empty. Cannot insert");
        }
        else {
            Nodes n = first;
            while(n.data != pos && n.next != null) {
                n = n.next;
            }
            if(n.data != pos){
                System.out.println("Position not found");
            }
            else {
                temp.next = n.next;
                n.next = temp;
            }
        }
    }

    public static void deleteBegin() {
        if(first == null) {
            System.out.println("List empty. Cannot delete");
        }
        else {
            first = first.next;
        }       
    }


    public static void deleteEnd() {
        if(first == null) {
            System.out.println("List empty. Cannot delete");
        }
        else {
            Nodes n = first;
            while(n.next.next != null) {
                n = n.next;
            }
            n.next = n.next.next;
        }
    }

    public static void deletePos(int pos) {
        if(first == null) {
            System.out.println("List empty. Cannot delete");
        }
        else {
            Nodes n = first;
            while(n.next.data != pos && n.next.next != null) {
                n = n.next;
            }
            if(n.next.data != pos) {
                System.out.println("Element not found. Deletion failed");
            }
            else{
                n.next = n.next.next;
            }
        }
    }

    public static void printAll() {
        System.out.println("Elements in link list");
        Nodes n = first;
        while(n != null) {
            System.out.print(n.data + "->");
            n = n.next;
        }
    }
}
Stephen
  • 1,737
  • 2
  • 26
  • 37
CRS
  • 471
  • 9
  • 23