Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to iterate through two generic lists with different types one item after another in java?

I want to create an iterator class which allows me to iterate through lists with generic types (e.g. lst1 integer, lst2 string) one item after another. For this I have to consider the following given situation.

The interface is a generic Iterator. This part of the code cannot be modified.

interface Iterator<E> {
E next ();
boolean hasNext();
}

The list class is also defined as following. The most important, a list object can return a iterator object with the method getIterator(). This part of the code cannot be modified.

class List<T> {
class ListNode {
    T val;
    ListNode next;

    ListNode (T v) {
        val = v; next = null;
    }
}

ListNode head;

List (ListNode hd) { head = hd; }
List () { this(null); }

void prepend (T val) {
    ListNode p = new ListNode(val);
    p.next = head;
    head = p;
}

//some other methods

class ListIterator implements Iterator<T> {
    ListNode pos;

    ListIterator () {
        pos = head;
    }

    public T next () {       
        T res = pos.val;
        pos = pos.next;
        return res;
    }

    public boolean hasNext () {
        return pos != null;
    }
}

Iterator<T> getIterator () {        
    return this.new ListIterator();
}
}

Lets assume both list have the same type and for now they have also the same length. I tried to create a class with two iterator objects and used the methods of the iterator objects to implement the interface Iterator. This part of the code is created by me and can be modified.

class ZipIterator<T> implements Iterator<T>
{
int counter;
Iterator<T> first;
Iterator<T> second;

ZipIterator (Iterator<T> f, Iterator<T> s)
{
    first = f;
    second = s;
    counter = 0;
}

public T next ()
{
    if (counter % 2 == 0)
    {
        counter++;
        return first.next();
    }

    else
    {
        counter++;
        return second.next();
    }

}
public boolean hasNext ()
{
    if (counter % 2 == 0)
        return first.hasNext();
    else
        return second.hasNext();
}
}

This works fine for two list with the same type. Here is the code and the output I used for the test:

class IteratorUtils
{
public static void main (String[] args)
{
    List<Integer> lst1 = new List<>();
    List<Integer> lst2 = new List<>();
    lst1.prepend(3);
    lst1.prepend(2);
    lst1.prepend(1);
    lst2.prepend(8);
    lst2.prepend(9);
    lst2.prepend(10);
    Iterator<Integer> it1 = lst1.getIterator();
    Iterator<Integer> it2 = lst2.getIterator();
    ZipIterator<Integer> zit = new ZipIterator<>(it1, it2);
    while (zit.hasNext())
    {
        System.out.println(zit.next());
    }
}
}

Output:

1
10
2
9
3
8

Now I want to implement the ZipIterator in a generic way, so I can use two lists with different types of items (e.g. integer and string). I know I have to change the class ZipIterator so the method next() returns a generic type but I don't know how. This is a university task i have to do and the prof has left a hint "use wild cards like: ? extends T, ? super T, ? extends Object". But with the wild cards i can only specify the types in or against the inherits direction, right? Is this possible to change the ZipIterator class that way so it accepts two iterator objects with different types?

like image 313
Toskavos Avatar asked Aug 13 '16 13:08

Toskavos


1 Answers

I won't give the full solution (and judging by your effort you don't want it), but I will attempt to explain in a way that will let you find it yourself.

First of all an unrelated note: you're specifying a specific iteration order. I assume this is fine and I will not touch it.

Your professor gave you the hint of using bounded generics. Let's understand why they are needed (see also the tutorial here and/or here). If you were asked to write a single method that takes an argument of any of 2 unknown types, your solution would be to find and take their common superclass - Object.

In generics the situation is similar - find the most common denominator, only the syntax is a bit more tricky. If you were to write the constructor

ZipIterator(Iterator<Object> f, Iterator<Object> s) {...}

and attempt the initialization

List<Integer> lst1 = new List<>();
List<String> lst2 = new List<>();
new ZipIterator(it1, it2);

you would get a compilation error (read it). That is because a List<String> is not a List<Object>, even though a String is an Object. The correct way to do this is

ZipIterator(Iterator<? extends Object> f, Iterator<? extends Object> s) {...}

where ? extends Object means "any type that extends Object" (which is all of them because Object...).

So you have the constructor, and you would need to make changes to your class in order to accommodate it. You don't even need to implement the given Iterator<E>, you just hold 2 of those like you already do. Lastly, the class itself need not have a generic type: since its next method must be able to return any type, it always returns Object.

If you have any questions during your future attempts at this problem, or you find that this solution doesn't fit the assignment's requirements, feel free to post a comment.

like image 196
user1803551 Avatar answered Sep 30 '22 19:09

user1803551