Итератор из объекта с помощью next () и get () - PullRequest
0 голосов
/ 17 мая 2018

Учитывая объект, подобный этому:

Matcher matcher = pattern.matcher(sql);

с использованием примерно так:

Set<String> matches = new HashSet<>();
while (matcher.find()) {
    matches.add(matcher.group());
}

Я бы хотел заменить этот цикл while чем-то более объектно-ориентированным, например, так:

new Iterator<String>() {
    @Override
    public boolean hasNext() {
        return matcher.find();
    }

    @Override
    public String next() {
        return matcher.group();
    }
}

, чтобы я мог легко, например, создать поток матчей, придерживаться использования API-интерфейсов и т. Д.

Дело в том, что я не знаю и не могу найтиболее краткий способ создать этот поток или итератор.Анонимный класс, подобный приведенному выше, слишком многословен на мой вкус.

Я надеялся найти что-то вроде IteratorFactory.from(matcher::find, matcher::group) или StreamSupport.of(matcher::find, matcher::group) в jdk, но пока не повезло.Я не сомневаюсь, что библиотеки, такие как Apache Commons или Guava, предоставляют что-то для этого, но, скажем, я не могу их использовать.

Есть ли удобная фабрика для Streams или Iterators, которая принимает комбинированный метод hasNext / next вJDK?

Ответы [ 2 ]

0 голосов
/ 18 мая 2018

Этот класс, который я написал, воплощает то, что я хотел найти в jdk. Видимо, хотя он просто не существует. принятый ответ Евгения предлагает решение java 9 Stream.

public static class SearchingIterator<T> implements Iterator<T> {
    private final BooleanSupplier advancer;
    private final Supplier<T> getter;

    private Optional<T> next;

    public SearchingIterator(BooleanSupplier advancer, Supplier<T> getter) {
        this.advancer = advancer;
        this.getter = getter;

        search();
    }

    private void search() {
        boolean hasNext = advancer.getAsBoolean();
        next = hasNext ? Optional.of(getter.get()) : Optional.empty();
    }

    @Override
    public boolean hasNext() {
        return next.isPresent();
    }

    @Override
    public T next() {
        T current = next.orElseThrow(IllegalStateException::new);
        search();
        return current;
    }
}

Использование:

Matcher matcher = Pattern.compile("\\d").matcher("123");
Iterator<String> it = new SearchingIterator<>(matcher::find, matcher::group);
0 голосов
/ 17 мая 2018

В java-9 вы можете сделать это с помощью:

Set<String> result = matcher.results()
            .map(MatchResult::group)
            .collect(Collectors.toSet());
System.out.println(result);

В java-8 вам понадобится резервный порт для этого, взятый из Сказочный ответ Хольгера

РЕДАКТИРОВАТЬ

Существует один метод tryAdvance, который может включать find/group, что-то вроде этого:

static class MyIterator extends AbstractSpliterator<String> {

    private Matcher matcher;

    public MyIterator(Matcher matcher) {
        // I can't think of a better way to estimate the size here
        // may be you can figure a better one here
        super(matcher.regionEnd() - matcher.regionStart(), 0);
        this.matcher = matcher;
    }

    @Override
    public boolean tryAdvance(Consumer<? super String> action) {
        while (matcher.find()) {
            action.accept(matcher.group());
            return true;
        }

        return false;
    }
}

И использование, например:

Pattern p = Pattern.compile("\\d");
Matcher m = p.matcher("12345");
Set<String> result = StreamSupport.stream(new MyIterator(m), false)
            .collect(Collectors.toSet());
...