如何在Iterable <T>中以最有效的方式使元素处于特定位置?

时间:2019-07-05 08:00:47

标签: java

我需要在Iterable<MyType>中的特定位置获取一个元素,以便不遍历Iterable<MyType>的所有元素,因为我知道所需元素位于哪个位置(据我所知有关循环遍历所有元素将花费O(n)时间,而访问特定元素将花费O(1)时间)。 这必须是最后一个元素之前的元素。 但是我找不到一种方法可以做到这一点。

public interface Iterable<T>显然没有方法可以在任意位置访问元素。

我尝试将Iterable<MyType>强制转换为List<MyType>,但是在运行时使用ClassCastException强制转换失败。因此,我不能使用ListIterator<E>,简单的List.get(E e)或某些自定义Function<T, U>来向后遍历元素或获取此元素(我打算做的这些事情)。

我当前的代码

// list.getItems() returns Iterable<MyType>
// I know that element I am looking for is at (iterable.size - 2) position
        for(MyType item : list.getItems()) {
            if (item.convertToText().matches(targetElementRegex)) {
                Pattern pattern = Pattern.compile(targetElementRegex);
                Matcher matcher = pattern.matcher(item.convertToText());
                if (matcher.find()) {
                    return Optional.of(Integer.parseInt(matcher.group(1)));
                }
            }
}

如您所见,尽管我确实知道要查找的目标元素在哪个位置,但是我只是循环浏览Iterable<T>中的所有元素,直到到达目标元素。 我想在Iterable<MyType>中的特定位置放置一个元素。

我想找出最有效的方法来做到这一点(或者至少是一种比当前解决方案更好的方法)。

UPD list是第三方库中一个类的实例,我没有写getItems(),也没有可以在{{1 }}类。

5 个答案:

答案 0 :(得分:1)

如dbl所述,您无法在Iterable对象的特定索引处获取元素。 如果计划将Iterable对象转换为list,则花费的时间相同(O(n)),再加上O(1)即可获取目标元素。 如果您真的很担心O(n)的时间,建议您直接进行迭代,直到目标元素(O(n-1))。

答案 1 :(得分:1)

Iterable不能让您提取给定位置的元素,这是设计使然。集合框架包含更多专门的类,以通过O(1)元素访问来处理顺序集合。这些是各种众所周知的列表实现,尤其是那些实现RandomAccess接口的列表。

如您所见,选择集合接口可能会产生很大的不同,尤其是在涉及O(xxx)表示法事物时。这是多功能性和性能之间的一种折衷。像Iterable这样的通用接口为您提供了最广泛的适用输入集,但是您只能获得RandomAccess集合的性能。

如果您要使用的所有输入都是RandomAccess集合(ArrayList实现了该集合),则没有理由将它们作为Iterable处理。如果不是这种情况,则可以在运行时检查此条件并选择最有效的算法。

答案 2 :(得分:0)

也许您可以尝试com.google.common.collect.Iterables。在此方法的描述中,您提供了Iterable和位置以获取特定的元素。

答案 3 :(得分:0)

使用Iterable接口,您无法在特定索引处获取元素。所有界面都允许您遍历Iterable中的所有项目,并观察那里有什么,仅此而已。您将必须手动管理当前位置(索引/光标)。一个简单的解决方案如下:

public static <T> T retrieveItemByIndex(Iterable<T> iterable, int index) {

    if (iterable == null || index < 0) {

        return null;
    }

    int cursor = 0;

    Iterator<T> iterator = iterable.iterator();

    while (cursor < index && iterator.hasNext()) {

        iterator.next();
        cursor++;
    }

    return cursor == index && iterator.hasNext() ? iterator.next() : null;
}

如果您不希望此辅助方法使用泛型,只需将其更改为仅与您的自定义类型一起使用,即可:

public MyType retrieveItemByIndex(Iterable<MyType> iterable, int index) {

    if (iterable == null || index < 0) {

        return null;
    }

    int cursor = 0;

    Iterator<MyType> iterator = iterable.iterator();

    while (cursor < index && iterator.hasNext()) {

        iterator.next();
        cursor++;
    }

    return cursor == index && iterator.hasNext() ? iterator.next() : null;
}

另一种方法是使用Stream API(Java 8及更高版本)。

首先,您必须从Iterable中获取流,然后跳过第一个index元素并找到第一个。如果索引超出范围,则将返回默认值。

int index = N - 2;
MyType defaultValue = null;

StreamSupport.stream(iterable.spliterator(), false)
    .skip(index)
    .findFirst()
    .orElse(defaultValue);

答案 4 :(得分:0)

如果只希望第二个倒数第二个位置,为什么在进入循环之前不直接用列表将该位置索引呢?代替list.getItems(),尝试list.getItem(list.getItemCount()-2)