如何在没有“丢失我的位置”的情况下访问迭代器之外的java集合中的元素?

时间:2011-01-29 04:28:03

标签: java collections iteration

我创建了一个名为Month的类,它扩展了Calendar,我用它来保存给定月份的事件。我有几年存储在TreeSet中的Month对象。我想记录的大多数事件持续数月,并且仅由其开始月份(以及持续时间,以月为单位)指定。我希望能够做到这一点:

for ( Event e : events )  
{  
    for ( Month aMonth : myMonths )  
    {  
         // check if this is a start month for e  
         // if it is, call aMonth.addEvent(e);  
         // and also add e to the next e.getDuration() months of myMonths  
         // and then start checking again from the month where we called addEvent(e)  
         // in case there is another occurrence of e starting
         // before the first one has finished
    }   
}

这是我遇到麻烦的首都。我尝试使用迭代器而不是foreach循环,并且在找到使用迭代器将e添加到下一个x月的开始日期时使用单独的循环,但是我无法将迭代器返回到它开始的位置。看起来ListIterator有一个previous()方法,但是我想使用SortedSet而不是List,以确保避免重复(尽管这种倾向可能是错误的?)

使用普通的旧数组执行此操作会感觉更容易,但该集合对程序的其他部分非常有用。 也许我可以使用多个迭代器,并根据需要将它们“用起来”,直到我主要的“书签”迭代器之后的几个月?虽然看起来并不优雅。 或者是否有“偷看”超出我的迭代器实际指向的位置?

我对这一切都很陌生,所以我可能只是犯了一个设计错误。欢迎所有建议!

3 个答案:

答案 0 :(得分:2)

Event和Month对象是什么样的?我认为你的工作会起作用:

for(Event event : events) {

   for(Month aMonth : myMonths) {

      if(aMonth >= event.startMonth && aMonth <= event.startMonth+event.duration) {
         aMonth.add(event);
      }

    }
}

或者你可以翻转它并转向其他方式,使你的外部迭代器成为月份,你的内部迭代器成为事件。这应该也可以,if()条件可能是相同的。

答案 1 :(得分:1)

根据您一次处理的事件数和月数,天真的方法可能是最好的。我首先让你的for循环处理迭代器,并接受你将进行(m * n)次迭代的事实。然后,如果您发现此位置导致显着减速,您可以尝试其他一些技术来加快速度,而不会使代码过于复杂。

尝试前进和后退将使您的代码难以理解并且更容易出错,而不必在性能方面获得更多。通常你不会注意到性能上的显着差异,直到你在两个集合中谈论至少数百个项目(在这种情况下,你可以从简单的事情开始,比如将数据分解成数年,例如,减少开销双嵌套for循环)。

修改

然而,由于我无法自拔,这是一个半优雅的策略,它将利用你的事件和月份都按升序存储的事实(我假设事件按照他们的顺序存储开始日期)。它使用LinkedList(在列表的前面和后面添加和删除元素非常有效)来跟踪当前事件可能跨越的月份,然后在找到事件没有的月份后立即中断。包括:

LinkedList<Month> monthList = new LinkedList<Month>();
var i = monthList.getIterator();
for(Event ev : events)
{
    shiftList(monthList, i, ev);
    for(Month m : monthList)
    {
        if (!isInMonth(ev, m)) break;
        m.addEvent(ev);
    }
}

...

// Remove months that are not in scope from the front of the list.
// Add months that are in scope to the end of the list
public void shiftList(LinkedList<Month> monthList, Iterator<Month> i, Event ev)
{
    while(!monthList.size() > 0 && !isInMonth(ev, monthList.getFirst()))
    {
        monthList.removeFirst();
    }
    while(i.hasNext() && isInMonth(ev, monthList.getLast()))
    {
        monthList.addLast(i.next());
    }
}

同样,你可以看到它有多复杂:我很可能在这个逻辑上引入了一个错误,如果没有彻底的单元测试,我会觉得在生产中使用它是不舒服的。在你有一个令人信服的理由进行优化之前,你通常要保持简单。

答案 2 :(得分:0)

Google的guava库提供了PeekingIterator,允许使用单元素的peekahead。通过Iterators.peekingIterator(Iterator)创建一个。