2017-02-15 13 views
1

У меня есть Map<String, Boolean> со следующими записями:Получить конкретную запись и его индекс с карты с помощью лямбда-выражения Java

{‘John’:false, 
'Mike':false, 
'Tom':true, 
'Harry':false, 
'Bob': false} 

Мне нужно найти первую запись и его индекс, который имеет истинное значение. Здесь я должен получить Tom и 3 (скажем, индекс начинается с 1).

я могу перебирать карту и получить эти значения:

int itr=0; 
for(Iterator<Map.Entry<String, Boolean>> entries = map.entrySet().iterator; entries.hasNext();) { 
    itr++; 
    Map.Entry<String, Boolean> entry = entries.next(); 
    if(entry.getValue()) { 
     name = entry.getKey(); 
     index = itr; 
    } 
} 

Однако я смотрю на лямбда-выражение для того же.

+5

'HashMap' не поддерживает порядок своих элементов! – user1803551

+2

@ user1639485 по индексу вы, вероятно, имеете в виду, сколько записей вы * видели *, прежде чем получили свою ценность? Если это так, это ненадежно, так как при определенном изменении размера карты эта запись может перемещаться. Если ваша карта не сохраняет заказ, например 'LinkedHashMap'. – Eugene

+0

@Eugene Я думаю, что вы не указали неправильный пользователь, и вы в основном сказали, что я сказал. – user1803551

ответ

3

Ну, если вы можете гарантировать, что карта на самом деле LinkedHashMap (так, что она сохраняет порядок вставки), вы можете сделать что-то вроде этого:

List<Map.Entry<String, Boolean>> l = map.entrySet().stream().collect(Collectors.toList()); 
    IntStream.range(0, l.size()) 
      .mapToObj(i -> new AbstractMap.SimpleEntry<>(i, l.get(i))) 
      .filter(e -> e.getValue().getValue()) 
      .map(e -> new AbstractMap.SimpleEntry<>(e.getValue().getKey(), e.getKey())) 
      .findFirst(); 
3

Я думаю, что это не представляется возможным выполнить все ниже условия:

  1. Решение должно быть ленивым (остановить перебор карты один раз ответа найден)
  2. решения не следует использовать явно итератор() или spliterator()
  3. Так lution должен соответствовать спецификации Stream API (в частности, промежуточные лямбда не должны иметь побочных эффектов)
  4. Решение не должно использовать сторонние потоковые расширения.

Если вы в порядке с нарушением № 1, проверьте ответ @ Евгения. Если вы в порядке с нарушением № 2, то ваш код в вопросе хорош. Если вы нормально с нарушением # 3, вы можете сделать что-то вроде этого:

AtomicInteger idx = new AtomicInteger(); 

String name = map.entrySet().stream() 
    .peek(e -> idx.incrementAndGet()) 
    .filter(Map.Entry::getValue) 
    .map(Map.Entry::getKey) 
    .findFirst().orElse(null); 
int itr = idx.get(); 

Если вы нормально нарушать # 4, вы можете рассмотреть возможность использования свободного StreamEx библиотеки:

Map.Entry<String, Integer> entry = StreamEx.of(map.entrySet()) // (name, bool) 
     .zipWith(IntStreamEx.ints().boxed()) // ((name, bool), index) 
     .filterKeys(Map.Entry::getValue) // filter by bool 
     .mapKeys(Map.Entry::getKey) // (name, index) 
     .findFirst() 
     .orElse(null); 
if(entry != null) { 
    String name = entry.getKey(); 
    int itr = entry.getValue(); 
} 
+1

оба варианта действительно приятные. один плюс. – Eugene

+1

Не имеет ли StreamEx 'takeWhile'? 'int index = StreamEx.of (map.values ​​()). takeWhile (b ->! b) .count(); String name = map.keySet(). Stream(). Skip (index) .findFirst() .Else (null); '... – Holger

+1

@ Хольджер, да, такое решение также возможно, хотя это двухпроходное. Btw, это хорошая версия, поскольку она доступна на Java 9. Также возможно с StreamEx 'long idx = StreamEx.ofValues ​​(map) .indexOf (b-> b) .orElse (-1)'. Другие однопроходные решения StreamEx также возможны как «EntryStream.of (map) .takeWhileInclusive (e ->! E.getValue()). Keys(). ZipWith (IntStreamEx.ints(). Boxed()). Reduce ((a, b) -> b). –

 Смежные вопросы

  • Нет связанных вопросов^_^