Java: получить первый элемент из коллекции

277

Если у меня есть коллекция, например Collection<String> strs, как я могу достать первый предмет? Я мог бы позвонить Iterator, взять ее сначала next(), а потом выбросить Iterator. Есть ли менее расточительный способ сделать это?

Ник Хейнер
источник
1
Конечно, может быть лучший способ получить доступ к первому элементу, если вы знаете, реализующий класс контейнера ...
Rooke
Обобщение для любого индекса: stackoverflow.com/questions/1047957/…
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
1
Похоже, вам нужен Queue.peek ()
Йоханнес

Ответы:

131

Iterables.get (yourC, indexYouWant)

Потому что на самом деле, если вы используете Коллекции, вы должны использовать Google Коллекции.

деревенщина
источник
7
Это делает то же самое, он просто проверяет, является ли он списком первым, и получает по индексу, если это так. У него также есть некоторый код, который пытается быстрее потерпеть неудачу в реальной Коллекции (то есть, если индекс слишком велик, он пытается выяснить это, не перебирая весь текст и не выдавая исключение в конце).
Ишай
1
Честно говоря, с точки зрения производительности это может быть немного медленнее, чем c.iterator (). Next () - но код гораздо понятнее и проще для модификации.
Карл
2
Я, конечно, согласен, что это чище, но ОП был бесполезным, но, думаю, поскольку ваш ответ был принят, это то, что было желательным.
Ишай
8
Для тех, кто (все еще) прибывает сюда: я думаю, что ответ jheddings, вероятно, является лучшим ответом «получи это», хотя я бы предпочел @ DonaldRaab (путь вниз по странице) для случаев, когда я уже использую библиотеку GC. Мой ответ на самом деле для случая, когда кто-то может захотеть писать с большей гибкостью на будущее (скажем, если кто-то решит, что второй элемент - это новый жар).
Карл
4
Иногда вы просто используете код, который использует Коллекции, так что делать нечего.
erickrf
436

Похоже, это лучший способ сделать это:

String first = strs.iterator().next();

Отличный вопрос ... На первый взгляд это похоже на недосмотр Collectionинтерфейса.

Обратите внимание, что «first» не всегда возвращает первое, что вы положили в коллекцию, и может иметь смысл только для упорядоченных коллекций. Может быть, поэтому нет get(item)звонка, поскольку порядок не обязательно сохраняется.

Хотя это может показаться немного расточительным, это может быть не так плохо, как вы думаете. На Iteratorсамом деле просто содержит информацию индексации в коллекции, а не копию всего собрания. Вызов этого метода создает экземпляр Iteratorобъекта, но на самом деле это единственные накладные расходы (не как копирование всех элементов).

Например, глядя на тип, возвращаемый ArrayList<String>.iterator()методом, мы видим, что это так ArrayList::Itr. Это внутренний класс, который просто обращается к элементам списка напрямую, а не копирует их.

Просто убедитесь, что вы проверили возврат, iterator()поскольку он может быть пустым или nullзависеть от реализации.

jheddings
источник
3
Важно отметить, что этот «трюк» работает только тогда, когда в коллекции действительно есть содержимое. Если он пуст, итератор может вернуть ошибку, при которой необходимо заранее проверить размер коллекции.
spaceemotion
20
Это должен быть правильный ответ. Я не понимаю, почему ответ всегда "использовать другую библиотеку!" ,
Кузеко
Как насчет второго элемента коллекции? Почему first-> next () не работает? Что я должен делать? Спасибо!
pb772
недостаточно безопасно, не гарантируется, что коллекция всегда будет указывать на 1-й элемент.
Следующий разработчик
84

В Java 8:

Optional<String> firstElement = collection.stream().findFirst();

Для более старых версий java есть метод getFirst в Guava Iterables :

Iterables.getFirst(iterable, defaultValue)
Виталий Федоренко
источник
6
Решение java 8 особенно полезно, потому что оно обрабатывает случай, когда коллекция изящно пуста.
SpaceTrucker
4
Не хорошо. Вы добавляете издержки stream (), чтобы получить get (0) только потому, что вам лень писать 4 строки кода. if (! CollectionUtils.isEmpty (productList)) {return Optional.of (productList.get (0)); } return Optional.empty ();
RS
У меня нет getFirstдоступных методов. Есть getи getLastметоды
user1209216
4
@RS и что произойдет, если вы не можете вызвать productList.get (0), так как это коллекция ..? (По вопросу ОП)
Денхам Кут
40

Не существует такого понятия, как «первый» элемент, Collectionпотому что это .. ну, просто коллекция.

Из метода Collection.iterator () Java-документа :

Нет никаких гарантий относительно порядка, в котором элементы возвращаются ...

Так что ты не можешь.

Если вы используете другой интерфейс, например List , вы можете сделать следующее:

String first = strs.get(0);

Но прямо из Коллекции это невозможно.

OscarRyz
источник
11
Я не думаю, что get(int n)определено дляCollection
Ник Хайнер
2
Вы правы, я скучаю по этому вопросу. Я обновил ответ. Ты не можешь! (если Коллекция не реализована каким-либо нижележащим классом, который позволяет предоставлять гарантию)
OscarRyz
получить не в интерфейсе коллекции
Энди Герна
21
Оскар, я думаю, ты преувеличиваешь дело. Первый элемент коллекции может быть произвольным в некоторых случаях, таких как HashSet, но он четко определен: это .iterator (). Next (). Это также стабильно , в каждой реализации коллекции, которую я когда-либо видел. (связано: обратите внимание, что хотя Set не гарантирует порядок, каждый подтип Set в JDK, кроме HashSet, делает это.)
Кевин Бурриллион,
3
Это может быть, но рассмотрим случай, когда вы добавляете новый элемент в коллекцию, вы не знаете (по интерфейсу), является ли этот элемент первым, последним или он будет вставлен в середину. Для точных результатов вы должны использовать другой интерфейс. Тем не менее, вероятно, что Росарху нужен первый элемент, несмотря ни на что. Знание лежащей в основе коллекции может помочь, но не позволит вам ее изменить.
ОскарРиз
4

Похоже, ваша коллекция хочет быть похожей на список, поэтому я бы предложил:

List<String> myList = new ArrayList<String>();
...
String first = myList.get(0);
Джим Ферранс
источник
2

В Java 8 у вас есть несколько операторов, например, limit

     /**
 * Operator that limit the total number of items emitted through the pipeline
 * Shall print
 * [1]
 * @throws InterruptedException
 */
@Test
public void limitStream() throws InterruptedException {
    List<Integer> list = Arrays.asList(1, 2, 3, 1, 4, 2, 3)
                               .stream()
                               .limit(1)
                               .collect(toList());
    System.out.println(list);
}
Павел
источник
2
Ответ @Vitalii Fedorenko stackoverflow.com/a/18165855/1562662 лучше.
Чако Мэтью
2

Guava предоставляет onlyElement Collector, но использует его, только если вы ожидаете, что в коллекции будет ровно один элемент.

Collection<String> stringCollection = ...;
String string = collection.stream().collect(MoreCollectors.onlyElement())

Если вы не знаете, сколько там элементов, используйте findFirst.

Optional<String> optionalString = collection.stream().findFirst();
cambunctious
источник
1

Вы можете сделать кастинг. Например, если существует один метод с этим определением, и вы знаете, что этот метод возвращает список:

Collection<String> getStrings();

И после вызова вам нужен первый элемент, вы можете сделать это так:

List<String> listString = (List) getStrings();
String firstElement = (listString.isEmpty() ? null : listString.get(0));
Начо Сориано
источник
0

Если вы знаете, что коллекция является очередью, вы можете легко преобразовать ее в очередь.

Есть несколько структур, которые вы можете использовать, чтобы получить заказ, но вам нужно будет привести его к нему.

Джеймс Блэк
источник
Я согласен, если вы не хотите повторяться, не используйте коллекцию. Вместо этого используйте другой, более конкретный интерфейс.
Адель Ансари
1
Интересно, хотя ... допустим, что лежащие в основе данные - это SortedSet, так что порядок имеет смысл, но у вас есть только представление коллекции (скажем, по глупой причине); если вы приведете коллекцию в список, очередь и т. д. и попытаетесь получить / poll / и т. д., произойдет ли катастрофа? Аналогично, если базовая структура представляет собой список, и так далее, и тому подобное.
Карл
@Cal - я не пробовал, но если вы приведете коллекцию к другому типу, чем она была изначально, вы должны получить ошибку, но я не пробовал, поэтому я могу ошибаться.
Джеймс Блэк
0

Это полностью зависит от того, какую реализацию вы использовали, будь то ArrayList связанный список или другие реализации набора.

если он установлен, то вы можете напрямую получить первый элемент, их можно сделать цикличным циклом по коллекции, создать переменную со значением 1 и получить значение, когда значение флага равно 1, после того, как этот цикл прервется.

если это реализация списка, то это легко определить с помощью индекса.

Синду Оад
источник
0

Функциональный способ:

public static <T> Optional<T> findFirst(List<T> result) {
    return Optional.ofNullable(result)
            .map(List::stream)
            .flatMap(Stream::findFirst);
}

Приведенный выше фрагмент кода сохраняется из NullPointerException и IndexOutOfBoundsException

Фархад Багиров
источник
1
Ваш выбор List<T>не удовлетворяет условие , что он должен работать для Collection<String>, но, конечно, может быть закреплена с помощью Collection<T>, с дополнительным изменением: .map(Collection::stream).
Scratte
-2

Вы могли бы сделать это:

String strz[] = strs.toArray(String[strs.size()]);
String theFirstOne = strz[0];

Javadoc для Collection дает следующее предостережение относительно упорядочивания элементов массива:

Если эта коллекция дает какие-либо гарантии относительно того, в каком порядке ее элементы возвращаются ее итератором, этот метод должен возвращать элементы в том же порядке.

Энди Герна
источник
2
Это создает новый массив String, намного дороже, чем создание итератора.
Джим Ферранс
Да, я думал об этом после того, как опубликовал это. Независимо от используемого метода, порядок зависит от базовой реализации Коллекции. «Сначала» становится относительным термином. Тем не менее, итератор () способ сделать это, вероятно, лучше в большинстве случаев.
Энди Герна
2
Я пришел сюда, потому что это было решение, которое я нашел и нашел безобразным.
haansn08