Поток Способ получения индекса первого элемента, совпадающего с логическим

У меня есть List<Users>, Я хочу получить индекс (первого) пользователя в потоке с определенным именем пользователя. Я не хочу на самом деле требовать User быть .equals() некоторым описанным Userпросто чтобы иметь то же имя пользователя.

Я могу придумать некрасивые способы сделать это (повторить и посчитать), но мне кажется, что должен быть хороший способ сделать это, возможно, с помощью Streams. Пока лучшее, что у меня есть:

int index = users.stream()
    .map(user -> user.getName())
    .collect(Collectors.toList())
    .indexOf(username);

Это не худший код, который я когда-либо писал, но он не очень хорош. Это также не так гибко, так как оно зависит от наличия функции отображения для типа с .equals() функция, которая описывает свойство, которое вы ищете; Я бы предпочел что-то, что могло бы работать на произвольную Function<T, Boolean>

Кто-нибудь знает как?

8 ответов

Решение

Изредка нет питона zipWithIndex в Яве. Вот я и наткнулся на что-то подобное:

OptionalInt indexOpt = IntStream.range(0, users.size())
     .filter(i -> searchName.equals(users.get(i)))
     .findFirst();

В качестве альтернативы вы можете использовать zipWithIndex из библиотеки протонпак

Заметка

Это решение может занять много времени, если users.get не работает с постоянным временем.

Попробуй это:

IntStream.range(0, users.size())
    .filter(userInd-> users.get(userInd).getName().equals(username))
    .findFirst()
    .get();

Использование библиотеки Guava: int index = Iterables.indexOf(users, u -> searchName.equals(u.getName()))

Вы можете попробовать библиотеку StreamEx, созданную Тагиром Валеевым. Эта библиотека имеет удобный метод #indexOf.

Это простой пример:

List<User> users = asList(new User("Vas"), new User("Innokenty"), new User("WAT"));
long index = StreamEx.of(users)
        .indexOf(user -> user.name.equals("Innokenty"))
        .getAsLong();
System.out.println(index);

Решение без внешней библиотеки

AtomicInteger i = new AtomicInteger(); // any mutable integer wrapper
int index = users.stream()
    .peek(v -> i.incrementAndGet())
    .anyMatch(user -> user.getName().equals(username)) ? // your predicate
    i.get() - 1 : -1;

посмотреть индекс инкремента i, пока предикат ложен, следовательно, когда предикат истинен, i на 1 больше, чем соответствующий предикат => i.get() -1

Здесь detectIndexв библиотеке Eclipse Collections, который принимаетPredicate.

int index = ListIterate.detectIndex(users, user -> username.equals(user.getName()));

Если у вас есть метод User класс, который возвращает boolean если username совпадения можно использовать следующие:

int index = ListIterate.detectIndexWith(users, User::named, username);

Примечание: я являюсь коммиттером для коллекций Eclipse.

Найти, если присутствует, затем вернуть результат на основе этого.

  1. Узнайте, присутствует элемент или нет.
  2. Если присутствует индекс возврата, в противном случае возвращается -1
          AtomicInteger index = new AtomicInteger();
    int result =  Arrays.stream(nums)
                         .peek((num) -> index.incrementAndGet())
                         .filter(num -> num == target)
                         .findFirst()
                         .orElse(-1);
    return result == -1 ? result : index.get()-1;
      AtomicInteger index = new AtomicInteger();

Начинается с 1, поэтому мы хотим получить индекс, поэтому мы вычитаем 1 из индекса

Больше короче

      return Arrays.stream(nums)
                     .peek((num) -> index.incrementAndGet())
                     .filter(num -> num == target)
                     .findFirst()
                     .orElse(-1) == -1 ? -1 : index.get()-1;

Пути ко всему индексу Перебрать индекс и получить весь индекс

      IntStream.range(0, list.size())
                .filter(i -> Objects.equals(list.get(i), target)).forEach(System.out::println);
Другие вопросы по тегам