如何在匹配后使用流找到项目?

时间:2015-08-03 13:58:39

标签: java regex lambda

使用Java流,很容易找到与给定属性匹配的元素  如:

 String b = Stream.of("a1","b2","c3")
     .filter(s -> s.matches("b.*"))
     .findFirst().get();
 System.out.println("b = " + b);

产地:
B = B2

然而,通常人们在匹配后想要一个或多个值,而不是匹配本身。我只知道如何用旧时尚圈来做到这一点。

    String args[] = {"-a","1","-b","2","-c","3"};
    String result = "";
    for (int i = 0; i < args.length-1; i++) {
        String arg = args[i];
        if(arg.matches("-b.*")) {
            result= args[i+1];
            break;
        }
    }
    System.out.println("result = " + result);

将产生:
结果= 2

使用Java 8 Streams有一种干净的方法吗?例如,将结果设置为&#34; 2&#34;给定上面的数组和谓词s -> s.matches("-b.*")

如果你可以获得下一个值,那么能够获得下一个N值或所有值的列表/数组,直到匹配另一个谓词,例如s -> s.matches("-c.*"),这也是有用的。

8 个答案:

答案 0 :(得分:3)

我通过这篇博文发现了它:
http://blog.jooq.org/2014/09/10/when-the-java-8-streams-api-is-not-enough/

名为jOOL的库有一个Github链接
 https://github.com/jOOQ/jOOL

和Maven中心信息在这里:
http://mvnrepository.com/artifact/org.jooq/jool/0.9.6

该示例的代码变为:

import org.jooq.lambda.Seq;

...

    String result = Seq.of(args)
            .skipWhile(s -> !s.matches("-b.*"))
            .skip(1)
            .findFirst()
            .get();

答案 1 :(得分:3)

这是用流解决这个问题的分裂器:

import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;
import java.util.Spliterators.AbstractSpliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class PartitioningSpliterator<E> extends AbstractSpliterator<List<E>>
{
  private final Spliterator<E> spliterator;
  private final int partitionSize;

  public PartitioningSpliterator(Spliterator<E> toWrap, int partitionSize) {
    super(toWrap.estimateSize(), toWrap.characteristics());
    if (partitionSize <= 0) throw new IllegalArgumentException(
        "Partition size must be positive, but was " + partitionSize);
    this.spliterator = toWrap;
    this.partitionSize = partitionSize;
  }

  public static <E> Stream<List<E>> partition(Stream<E> in, int size) {
    return StreamSupport.stream(new PartitioningSpliterator(in.spliterator(), size), false);
  }

  @Override public boolean tryAdvance(Consumer<? super List<E>> action) {
    final HoldingConsumer<E> holder = new HoldingConsumer<>();
    if (!spliterator.tryAdvance(holder)) return false;
    final ArrayList<E> partition = new ArrayList<>(partitionSize);
    int j = 0;
    do partition.add(holder.value); while (++j < partitionSize && spliterator.tryAdvance(holder));
    action.accept(partition);
    return true;
  }

  @Override public long estimateSize() {
    final long est = spliterator.estimateSize();
    return est == Long.MAX_VALUE? est
         : est / partitionSize + (est % partitionSize > 0? 1 : 0);
  }

  static final class HoldingConsumer<T> implements Consumer<T> {
    T value;
    @Override public void accept(T value) { this.value = value; }
  }
}

一旦你把它隐藏在项目的某个地方,你可以说

partition(Stream.of("-a","1","-b","2","-c","3"), 2)
      .filter(pair -> pair.get(0).equals("-b"))
      .findFirst()
      .map(pair -> pair.get(1))
      .orElse("");

作为一个侧面点,所提出的分词符依赖于trySplitAbstractSpliterator的默认实现来支持并行性。

答案 2 :(得分:1)

我不能说以下是有效的,但它遵循一个容易应用的模式。 (但在实际的函数式语言中,添加过滤器时效率可能会很高。)

首先从字符串流中收集[[-c, 3], [-b, 2], [-a, 1]]

    List<List<String>> optionLists = Stream.of("-a","1","-b","2","-c","3")
            .collect(ArrayList<List<String>>::new,
                    (lists, arg) -> {
                        if (arg.startsWith("-")) {
                            List<String> list = new LinkedList<>();
                            list.add(arg);
                            lists.add(0, list);
                        } else {
                            List<String> list = lists.get(0);
                            list.add(arg);
                        }
                    },
                    List::addAll);
    System.out.println(optionLists);

然后可以在地图中将其转换为所有选项。

    List<String> bargs = optionLists.stream()
            .collect(Collectors.toMap(lst -> lst.get(0),
                    lst -> lst.subList(1, lst.size()))).get("-b");
    System.out.println("For -b: " + bargs);

答案 3 :(得分:1)

据我所知,这似乎并不是一种简单的方法,其中很多原因都源于Java的Stream API缺乏的缺点。获取流元素的索引以及将流压缩在一起的能力。

如果我们可以获取流中某个元素的索引,然后只需使用与.limit(n)函数配对的.skip()函数来丢弃这些元素,我们可以想象一个简单的问题解决方案。包括所需的匹配点,然后将结果限制为下一个n个元素。

您可能需要查看Protonpack,这是一个用于Java 8的&#34; Streams实用程序库,提供takeWhile,skipWhile,zip和展开。&#34;使用此库可以轻松解决问题:

Stream<String> stringStream = Stream.of("-a","1","-b","2","-c","3");
Stream<String> nAfterMatch = 
    StreamUtils.skipWhile(stringStream, s -> !(s.matches("-b.*")))
    .limit(n);

答案 4 :(得分:0)

对于您希望直接使用som PairTuple类之后的值的情况。

String b = Stream.of(Pair.of("-a","1"),Pair.of("-b","2"),Pair.of("-c","3"))
    .filter(p -> p.first.matches("-b.*"))
    .map(p -> p.second)
    .findFirst().get();

答案 5 :(得分:0)

这可以是解决方案解决方案:

String[] args = {"-a","1","-b","2","-c","3"};

String b = IntStream.range(1, args.length)
   .mapToObj(i -> new Pair<>(args[i-1], args[i]))
   .filter(pp -> pp.getFirst().startsWith("-b"))
   .findFirst().get().getSecond();

System.out.println("b = " + b);
//=> Output: b = 2

.mapToObj用于使用来自输入数组args的连续索引从args数组生成字符串值对。

答案 6 :(得分:0)

由于Java流takeWhile,有可能:

Stream<String> strings = Stream.of("a1","b2","c3");
final AtomicBoolean matchFound = new AtomicBoolean(false);
final AtomicReference<String> stringAfterMatch = new AtomicReference<>();
strings.takeWhile(string -> stringAfterMatch.get() == null).forEach(string -> {
  if (matchFound.get()) {
      stringAfterMatch.set(string);
  }
  if (string.matches("b.*")) {
    matchFound.set(true);
  }
});
String b = stringAfterMatch.get();

答案 7 :(得分:0)

private static boolean containsWords(String input, String[] words) {
        
        Arrays.stream(words).allMatch(new Predicate<String>() {

            @Override
            public boolean test(String t) {
                if(input.contains(t))
                    counter+=1; //add  a global counter
                //ExtensionUtils.keyWordHash.put(t, input); // or you can put the strings to a hashmap and retrieve later
                return input.contains(t);
            }
        });
        return Arrays.stream(words).allMatch(input::contains);
    }