【问题标题】:How to iterate through two generic lists with different types one item after another in java?java - 如何在java中逐个遍历具有不同类型的两个通用列表?
【发布时间】:2016-08-13 13:37:12
【问题描述】:

我想创建一个迭代器类,它允许我一个接一个地遍历具有泛型类型(例如 lst1 整数、lst2 字符串)的列表。 为此,我必须考虑以下给定的情况。

接口是一个通用的迭代器。这部分代码不能修改。

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

列表类也定义如下。最重要的是,列表对象可以通过 getIterator() 方法返回一个迭代器对象。这部分代码不能修改。

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();
}
}

让我们假设两个列表具有相同的类型,并且现在它们也具有相同的长度。我尝试用两个迭代器对象创建一个类,并使用迭代器对象的方法来实现接口Iterator。这部分代码是我自己创建的,可以修改。

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();
}
}

这适用于具有相同类型的两个列表。这是我用于测试的代码和输出:

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());
    }
}
}

输出:

1
10
2
9
3
8

现在我想以通用方式实现 ZipIterator,因此我可以使用两个具有不同类型项目(例如整数和字符串)的列表。我知道我必须更改类 ZipIterator 以便方法 next() 返回一个泛型类型,但我不知道如何。 这是我必须做的大学任务,教授留下了提示“使用通配符,例如:?扩展 T,?超级 T,?扩展对象”。但是使用通配符我只能指定继承方向中的类型或反对继承方向,对吗?是否可以通过这种方式更改 ZipIterator 类,使其接受两个不同类型的迭代器对象?

【问题讨论】:

  • 这两种类型有什么限制吗?如果没有,你能做的最好的就是返回Object
  • 您发布的代码的哪一部分是作业中给定的,您可以修改哪一部分?
  • 好吧,我可以给出答案,但这会消除作业的所有困难。不过我可以给个提示,看看PECS
  • Iterator 和 List 类是给定的,不能修改。所有其他代码都是我制作的,可以修改。我会尝试返回 Object 并检查 PECS。
  • 要明确(我目前正在写一个答案,这个问题出现了),你必须在课堂上有implements Iterator&lt;...&gt;,还是你自己做的?

标签: java list generics iterator wildcard


【解决方案1】:

我不会给出完整的解决方案(根据你的努力判断你不想要它),但我会尝试以一种让你自己找到的方式来解释。

首先是一个不相关的注释:您正在指定特定的迭代顺序。我认为这很好,我不会碰它。

您的教授给了您使用有界泛型的提示。让我们了解为什么需要它们(另请参阅tutorial here 和/或here)。如果你被要求编写一个方法来接受 2 种未知类型中的任何一种的参数,你的解决方案是找到并采用它们的公共超类 - Object

在泛型中情况类似——找到最常见的分母,只是语法有点棘手。如果你要写构造函数

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

并尝试初始化

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

你会得到一个编译错误(阅读它)。这是因为List&lt;String&gt; 不是List&lt;Object&gt;,即使StringObject。正确的做法是

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

其中? extends Object 表示“任何扩展Object 的类型”(这是所有类型,因为Object...)。

所以你有构造函数,你需要对你的类进行更改以适应它。你甚至不需要实现给定的Iterator&lt;E&gt;,你只需要像你已经做的那样持有其中的两个。最后,类本身不需要有泛型类型:因为它的next 方法必须能够返回任何类型,所以它总是返回Object

如果您在以后尝试解决此问题时有任何疑问,或者您发现此解决方案不符合作业要求,请随时发表评论。

【讨论】:

    【解决方案2】:

    我知道我必须更改类 ZipIterator,以便方法 next() 返回一个泛型类型,但我不知道如何。

    这不太对。由于ZipIterator&lt;T&gt; 扩展了Iterator&lt;T&gt;,因此它的next() 方法必须返回T 实际上是一成不变的。这是有道理的:迭代器类型参数的全部意义在于让您指定其next() 方法将返回的类型。

    相反,您的教授想要的是能够从两个具有不同类型参数的迭代器中构造 ZipIterator&lt;...&gt;。例如,他希望能够写作:

    List<Integer> listOfIntegers = ...;
    List<String> listOfStrings = ...;
    
    ZipIterator<Object> zipIterator =
        new ZipIterator<>(listOfIntegers.getIterator(), listOfStrings.getIterator());
    

    请注意,由于zipIterator.next() 有时会返回Integer,有时会返回String,因此我们必须使用ZipIterator&lt;Object&gt; 之类的东西,它允许两种可能性。其他选项包括ZipIterator&lt;Serializable&gt;ZipIterator&lt;Comparable&lt;?&gt;&gt;,因为Integer-s 和String-s 都是SerializableComparable&lt;?&gt;


    因此,您的教授希望您解决的问题是,在您当前的代码中,您的 构造函数 要求两个迭代器具有完全相同的类型参数(彼此相同,并且作为 ZipIterator本身):

    ZipIterator (Iterator<T> f, Iterator<T> s)
    

    你知道如何解决这个问题吗?

    【讨论】:

    • 阅读问题的 cmets。您最初的假设并不那么准确。
    • @user1803551:我确实阅读了 cmets,我坚持我的“假设”。 implements Iterator&lt;T&gt; 对设计至关重要,我们不应该仅仅因为教授把它留给学生写就扔掉它。也许学生可以通过放弃这一点并在作业中获得部分学分来找到“简单的出路”,但您为什么要鼓励这样做呢?
    • 如果它是设计,那么它不是“一成不变的”——它是一种选择。我不明白你为什么认为不实施它会导致部分功劳。我们没有关于分级方法的信息,也没有任何具体要求。我也不明白为什么放弃它是一个“简单的出路”,如果没有必要那么放弃它是正确的选择。如果有的话,教授建议在解决方案中使用通配符。
    • @user1803551:next() 的返回类型是一成不变的,因为ZipIterator&lt;T&gt;(正确)实现了Iterator&lt;T&gt;。回复:“我不明白你为什么认为不实施它会导致部分学分”:显然,这项作业的目的是了解泛型和通配符。因此,如果 OP 在它们显然是正确的方法时明确避免泛型,仅仅因为(s)他无法让它们工作,我希望教授会减掉几分。回复:“教授提示在解决方案中使用通配符”:是的,绝对的。我的回答依赖于此。 ;-)
    【解决方案3】:

    据我了解,您想要迭代不同类型的列表,解决方案之一是让您的构造函数接受迭代器满足它是扩展对象的任何事物的迭代器的要求,但这会将您对检索到的项目的使用限制为将其用作对象,否则您将不得不强制转换它们以完成进一步的任务,一种限制较少的方法是使构造函数接受迭代器满足要求它是任何扩展最近共同祖先的事物的迭代器,例如 ZipIterator(Iterator&lt;? extends T&gt; f, Iterator&lt;? extends T&gt; s) 所以你的类会是这样的

    class ZipIterator<T> implements Iterator<T> {
    
        int counter;
        Iterator<? extends T> first;
        Iterator<? extends T> second;
    
        ZipIterator(Iterator<? extends T> f, Iterator<? extends T> s) {
            first = f;
            second = s;
            counter = 0;
        }
    
        @Override
        public T next() {
            if (counter % 2 == 0) {
                counter++;
                return first.next();
            } else {
                counter++;
                return second.next();
            }
        }
    
        @Override
        public boolean hasNext() {
            if (counter % 2 == 0) {
                return first.hasNext();
            } else {
                return second.hasNext();
            }
        }
    
    }
    

    然后要使用它,你可以指定两种类型都收敛到它的最合适的超类,如果它是Object,你可以写ZipIterator&lt;Object&gt; zit =,下面的代码将显示一个任意用例

            List<StringBuilder> bl= Arrays.asList(new StringBuilder("hi i'm builder"));
            List<String> sl = Arrays.asList("hi i'm string");
            ZipIterator<CharSequence> zit = new ZipIterator<>(bl.iterator(), sl.iterator());
            while (zit.hasNext()) {
                CharSequence cs = zit.next();
                System.out.println(cs.subSequence(6,cs.length()));
            }
    

    【讨论】:

      【解决方案4】:

      感谢您的帮助。我学到了很多东西。这是我的解决方案和对该任务的更多解释。

      请首先注意,ZipIterator 类的设计并非一成​​不变。 ZipIterator 是我设计的。可能还有其他解决方案,但这是我的尝试。

      指定任务:“请构造一个带有多个方法的类IteratorUtils。方法zip接收两个迭代器对象并返回一个迭代器对象,该迭代器对象交替迭代接收到的两个迭代器对象的项。zip返回的迭代器函数应该在较短的迭代器对象的最后一项之后停止。使用通配符,以便您可以将 zip 函数应用于不同类型的迭代器对象。"

      为此,我首先创建了 IteratorUtils 类。请注意,拉链功能的设计也不是一成不变的。在任务中它只说:“方法 zip 接收两个迭代器对象并返回一个迭代器对象,该迭代器对象交替迭代接收到的两个迭代器对象的项目。”

      class IteratorUtils
      {
      static ZipIterator zip (Iterator<? extends Object> first, Iterator<? extends Object> second)
      {
          return new ZipIterator(first, second);
      }
      }
      

      然后我创建了 ZipIterator 类。在阅读了您的答案和一些教程后,我理解了此任务中有界类型参数的含义。就像 user1803551 说的那样,ZipIterator 类不应该是通用的。我只需要意识到我必须找到共同的超类(这里是 Object)。所以我不得不将我的 ZipIterator 类更改为以下内容:

      class ZipIterator
      {
      int counter;
      Iterator first;
      Iterator second;
      
      ZipIterator (Iterator<? extends Object> f, Iterator<? extends Object> s)
      {
          first = f;
          second = s;
          counter = 0;
      }
      
      public Object 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();
      }
      }
      

      在我的主要方法中,我使用以下代码:

      public static void main (String[] args)
      {
          List<Integer> lst1 = new List<>();
          List<String> lst2 = new List<>();
          lst1.prepend(3);
          lst1.prepend(2);
          lst1.prepend(1);
          lst2.prepend("three");
          lst2.prepend("two");
          lst2.prepend("one");
          Iterator<Integer> it1 = lst1.getIterator();
          Iterator<String> it2 = lst2.getIterator();
          ZipIterator zit = zip(it1, it2);
          while (zit.hasNext())
          {
              System.out.println(zit.next());
          }
      }
      

      输出:

      1
      one
      2
      two
      3
      three
      

      【讨论】:

      • 好!关于您的解决方案的一件事:不要将 Iterator 字段保留为原始类型 - 也使它们成为通用类型。关于您的问题的一件事:您在此处的答案中添加了明确的规则,但是如果您将它们添加到问题中以避免混淆,那将会有所帮助。最佳做法是准确引用作业。
      猜你喜欢
      • 1970-01-01
      • 2022-01-19
      • 2013-04-08
      • 1970-01-01
      • 1970-01-01
      • 2011-08-03
      • 2021-10-08
      • 2023-04-02
      • 2021-10-12
      相关资源
      最近更新 更多