Как наиболее эффективно получить элемент в определенной позиции в Iterable <T>? - PullRequest
0 голосов
/ 05 июля 2019

Мне нужно получить элемент в определенной позиции в 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() и не могу добавить что-то новое в list class.

Ответы [ 5 ]

2 голосов
/ 05 июля 2019

Iterable не дает вам способа извлечь элемент в заданной позиции, и это по замыслу.Каркас коллекций содержит более специализированные классы для обработки последовательных коллекций с доступом к элементу O(1).Это различные хорошо известные реализации списков, особенно те, которые реализуют интерфейс RandomAccess.

Как видите, выбор интерфейса коллекции может иметь большое значение, особенно когда дело касается O(xxx) обозначений.Это своего рода компромисс между универсальностью и производительностью.Универсальный интерфейс, такой как Iterable, предоставляет вам самый широкий набор применимых входных данных, но вы получаете производительность только для RandomAccess коллекций.

Если все входные данные, которые вы собираетесь использовать, являются RandomAccess коллекциями (ArrayList реализуетэто) нет причины обращаться с ними как Iterable.Если это не так, вы можете проверить это условие во время выполнения и выбрать наиболее эффективный алгоритм.

1 голос
/ 05 июля 2019

С интерфейсом 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);
1 голос
/ 05 июля 2019

Как уже упоминалось в dbl, вы не можете получить элемент по определенному индексу для объекта Iterable. Если вы планируете преобразовать объект Iterable в список, это то же самое время (O (n)), потраченное, плюс O (1), чтобы получить целевой элемент. Если вы действительно беспокоитесь о своем времени O (n), я предлагаю вам просто повторить его до тех пор, пока ваш целевой элемент (O (n-1)).

0 голосов
/ 05 июля 2019

Почему бы вам просто не индексировать эту позицию в списке перед входом в цикл, если вы просто хотите, чтобы от 2-ой до последней позиции?Вместо list.getItems () попробуйте list.getItem (list.getItemCount () - 2)

0 голосов
/ 05 июля 2019

Возможно, вы могли бы попробовать com.google.common.collect.Iterables .В описании этого метода у вас есть то, что вы предоставляете Iterable и позицию для получения конкретного элемента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...