我需要在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 }}类。
答案 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)