0
@Override
public void intersect(Set<E> set) {

    Iterator<E> iteratorSet = set.iterator();
    Iterator<E> thisIterator = this.iterator();

        
    SetNode<E> head = new SetNode<>(null,null);
    SetNode<E> curNode = head;

    Optional<E> optionalSet = getOptional(iteratorSet);
    Optional<E> optionalThis = getOptional(thisIterator);

    while (optionalSet.isPresent() && optionalThis.isPresent()) {
        SetNode<E> nextNode;
        int compare = optionalThis.get().compareTo(optionalSet.get());
        if(compare == 0) {
            nextNode = new SetNode<>(optionalThis.get());
            curNode.next = nextNode;
            curNode = nextNode;
            optionalThis = getOptional(thisIterator);
            optionalSet = getOptional(iteratorSet);
        }
        else if(compare < 0)
            optionalThis = getOptional(thisIterator);

        else
            optionalSet = getOptional(iteratorSet);

    }
    this.head = head.next;

}

In my university I was supposed to create a ListSet class that has a intersect function, that takes in the intersect of two set that are sorted. I don't understand why we need to do

    SetNode<E> head = new SetNode<>(null,null);
    SetNode<E> curNode = head;
Sören
  • 1,803
  • 2
  • 16
  • 23

2 Answers2

0

I'm guessing you were supposed to create a DoublyLinkedList with Set functionality for your task. Here is a visualization of it: http://bridgesuncc.github.io/tutorials/DoublyLinkedList.html

Linked List is storing a reference to the first(head) element of the list instead of storing all the references in an array(the way ArrayList does it). I'm guessing this head element is used as a constructor parameter later in the code, something like:

LinkedSet<E> result = new LinkedSet<>(head);

While currNode is used to store reference to the current element and get's updated in the cycle.

k4rnaj1k
  • 105
  • 7
0

When adding a new node, you normally need to distinguish between the case where the list is empty (and the new node becomes the head) and the case where it is not empty (and the head remains what it was). By using a dummy node, you actually never have that empty-list case (since there is that one node), and so that distinction does not have to be made in the rest of the code. Only at the very end you throw away that dummy node and return the first node that follows it, which really is the head of the new list.

To compare, here is how the code would look if you would not use a dummy node (comments indicate where there are changes):

    Iterator<E> iteratorSet = set.iterator();
    Iterator<E> thisIterator = this.iterator();
    
    SetNode<E> head = null; // No dummy node
    SetNode<E> curNode; // No need to initialise

    Optional<E> optionalSet = getOptional(iteratorSet);
    Optional<E> optionalThis = getOptional(thisIterator);

    while (optionalSet.isPresent() && optionalThis.isPresent()) {
        SetNode<E> nextNode;
        int compare = optionalThis.get().compareTo(optionalSet.get());
        if(compare == 0) {
            nextNode = new SetNode<>(optionalThis.get());
            if (head == null) {  // Now we need to deal with this scenario
                head = nextNode; // This only happens once
            } else {
                curNode.next = nextNode;
            }
            curNode = nextNode;
            optionalThis = getOptional(thisIterator);
            optionalSet = getOptional(iteratorSet);
        }
        else if(compare < 0)
            optionalThis = getOptional(thisIterator);
        else
            optionalSet = getOptional(iteratorSet);
    }
    // After the loop, there's now no need to drop a first dummy node.
    this.head = head;

It is a matter of taste which version you would prefer to use. Each has its own advantages/disadvantages, including:

  • The dummy version needs the extra memory for the dummy node, but it is a constant amount, so some would consider this is not significant.

  • The dummy version has less logic in the loop: one less comparison to make. This is the main reason some would go for this, as the loop is potentially executed many times and any saving you can make there has a bigger impact.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • Thank you I found this very helpful, but I still don't understand how the head node is linked too the rest of the curNodes – Tobias Chisi May 29 '23 at 12:38
  • OK, but that is a different question. In your code `curNode` initially references the same node as `head`, and so the upcoming assignment to `curNode.next` (in the first iteration of the loop) is setting the `next` property of the dummy node. And so it continues in the next iteration where the `next` property of that `head.next` node is set, ...etc. The invariant of the loop is that `curNode` is the tail of the merged list, and `head` is the predecessor of the actual head of that list. – trincot May 29 '23 at 19:40