Regex-minták előzetes fordítása minta-objektumokba

1. Áttekintés

Ebben az oktatóanyagban meglátjuk a a regex minta előzetes összeállításának előnyei és a a Java 8-ban és 11-ben bevezetett új módszerek.

Ez nem lesz regex útmutató, de erre a célra kiváló útmutatónk van a Java rendszeres kifejezésekhez.

2. Előnyök

Az újrafelhasználás elkerülhetetlenül teljesítménynövelést eredményez, mivel nem kell időről időre létrehoznunk és újrateremtenünk ugyanazon tárgyakat. Tehát feltételezhetjük, hogy az újrafelhasználás és a teljesítmény gyakran összekapcsolódik.

Vessünk egy pillantást erre az elvre, amint az vonatkozik rá # Minta összeállítása. WEgyszerű benchmarkot fogok használni:

  1. Van egy listánk 5 000 000 számmal 1-től 5 000 000-ig
  2. Regexünk páros számokkal fog egyezni

Tehát teszteljük ezeket a számokat a következő Java regex kifejezésekkel:

  • String.matches (regex)
  • Pattern.matches (regex, charSequence)
  • Pattern.compile (regex) .matcher (charSequence) .matches ()
  • Előre lefordított regex sok hívással preCompiledPattern.matcher (érték) .matches ()
  • Előre összeállított regex egy Matcher példány és sok hívás matcherFromPreCompiledPattern.reset (érték) .matches ()

Valójában, ha megnézzük a # Karakterlánc egyezikVégrehajtása:

nyilvános logikai egyezések (String regex) {return Pattern.matches (regex, this); }

És itt # Minta egyezik:

nyilvános statikus logikai egyezések (String regex, CharSequence input) {Pattern p = compile (regex); Matcher m = p.matcher (bemenet); return m.matches (); }

Aztán ezt el tudjuk képzelni az első három kifejezés hasonlóan fog teljesíteni. Ez azért van, mert az első kifejezés a másodikat, a második a harmadikat hívja.

A második pont az, hogy ezek a módszerek nem használják fel újra a Minta és Matcher létrehozott példányok. És ahogy a referenciaértékben látni fogjuk, ez hatszorosára rontja a teljesítményt:

 @Benchmark public void matcherFromPreCompiledPatternResetMatches (Blackhole bh) {for (String value: values) {bh.consume (matcherFromPreCompiledPattern.reset (value) .matches ()); }} @Benchmark public void preCompiledPatternMatcherMatches (Blackhole bh) {for (String value: values) {bh.consume (preCompiledPattern.matcher (value) .matches ()); }} @Benchmark public void patternCompileMatcherMatches (Blackhole bh) {for (String value: values) {bh.consume (Pattern.compile (PATTERN) .matcher (value) .matches ()); }} @Benchmark public void patternMatches (Blackhole bh) {for (String value: values) {bh.consume (Pattern.matches (PATTERN, value)); }} @Benchmark public void stringMatchs (Blackhole bh) {Azonnali indítás = Azonnali.now (); for (String érték: értékek) {bh.cumsume (value.matches (PATTERN)); }} 

A benchmark eredményeket tekintve ez kétségtelen előre összeállítva Minta és újrafelhasználta Matcher a nyertesek több mint hatszor gyorsabb eredménnyel:

Benchmark Mode Cnt Score Error egységek PatternPerformanceComparison.matcherFromPreCompiledPatternResetMatches avgt 20 278,732 ± 22,960 ms / op PatternPerformanceComparison.preCompiledPatternMatcherMatches avgt 20 500,393 ± 34,182 ms / op PatternPerformanceComparison.stringMatchs avgt 20 1433,099 ± 73,687 ms / op PatternPerformanceComparison.patternCompileMatcherMatches avgt 20 1774,429 ± 174,955 ms / op PatternPerformanceComparison.patternMatches avgt 20 1792.874 ± 130.213 ms / op

A teljesítményidőn túl a létrehozott objektumok száma is megvan:

  • Az első három forma:
    • 5,000,000 Minta létrehozott példányok
    • 5,000,000 Matcher létrehozott példányok
  • preCompiledPattern.matcher (érték) .matches ()
    • 1 Minta létrehozott példány
    • 5,000,000 Matcher létrehozott példányok
  • matcherFromPreCompiledPattern.reset (érték) .matches ()
    • 1 Minta létrehozott példány
    • 1 Matcher létrehozott példány

Tehát ahelyett, hogy átruháznánk a regexünket # Karakterlánc egyezik vagy # Minta egyezik hogy mindig létrehozza a Minta és Matcher példányok. Előre össze kell állítanunk a regexünket a teljesítmény elérése érdekében, és kevesebb objektumot kell létrehozni.

Ha többet szeretne tudni a regex teljesítményéről, olvassa el a Java rendszeres kifejezések teljesítményének áttekintését.

3. Új módszerek

A funkcionális interfészek és adatfolyamok bevezetése óta az újrafelhasználás könnyebbé vált.

A Minta osztály új Java verziókban fejlődött integráció biztosítása a folyamokkal és a lambdákkal.

3.1. Java 8

A Java 8 két új módszert vezetett be: splitAsStream és asPredikát.

Nézzünk meg néhány kódot a splitAsStream amely létrehoz egy adatfolyamot az adott bemeneti szekvenciából a minta egyezései köré:

@Test public void givenPreCompiledPattern_whenCallSplitAsStream_thenReturnArraySplitByThePattern () {Pattern splitPreCompiledPattern = Pattern.compile ("__"); Stream textSplitAsStream = splitPreCompiledPattern.splitAsStream ("SajátNév__is__Fabio_Silva"); Karakterlánc [] textSplit = textSplitAsStream.toArray (String [] :: új); assertEquals ("SajátNév", textSplit [0]); assertEquals ("is", textSplit [1]); assertEquals ("Fabio_Silva", textSplit [2]); }

A asPredikát A metódus olyan predikátumot hoz létre, amely úgy viselkedik, mintha egy illesztőt hozna létre a bemeneti szekvenciából, majd meghívja a keresést:

karakterlánc -> egyező (karakterlánc) .find ();

Hozzunk létre egy mintát, amely megegyezik a listán szereplő nevekkel, amelyek legalább utó- és vezetéknevekkel rendelkeznek, egyenként legalább három betűvel:

@Test public void givenPreCompiledPattern_whenCallAsPredicate_thenReturnPredicateToFindPatternInTheList () {List namesToValidate = Arrays.asList ("Fabio Silva", "Silva úr"); Pattern firstLastNamePreCompiledPattern = Pattern.compile ("[a-zA-Z] {3,} [a-zA-Z] {3,}"); Predikálja a mintákatAsPredicate = firstLastNamePreCompiledPattern.asPredicate (); List validNames = namesToValidate.stream () .filter (patternsAsPredicate) .collect (Collectors.toList ()); assertEquals (1, validNames.size ()); assertTrue (validNames.contains ("Fabio Silva")); }

3.2. Java 11

A Java 11 bemutatta a asMatchPredicate módszer állítmányt hoz létre, amely úgy viselkedik, mintha egy illesztőt hozna létre a bemeneti szekvenciából, majd hívja az egyezéseket:

string -> matcher (string) .matches ();

Hozzunk létre egy mintát, amely megegyezik a listán szereplő nevekkel, amelyeknek csak az első és a vezetékneve van, legalább három betűvel:

@Test public void givenPreCompiledPattern_whenCallAsMatchPredicate_thenReturnMatchPredicateToMatchesPattern () {List namesToValidate = Arrays.asList ("Fabio Silva", "Fabio Luis Silva"); Pattern firstLastNamePreCompiledPattern = Pattern.compile ("[a-zA-Z] {3,} [a-zA-Z] {3,}"); Predicate patternAsMatchPredicate = firstLastNamePreCompiledPattern.asMatchPredicate (); List validatedNames = namesToValidate.stream () .filter (patternAsMatchPredicate) .collect (Collectors.toList ()); assertTrue (validatedNames.contains ("Fabio Silva")); assertFalse (validatedNames.contains ("Fabio Luis Silva")); }

4. Következtetés

Ebben az oktatóanyagban azt láttuk, hogy a Az előre összeállított minták használata sokkal jobb teljesítményt nyújt nekünk.

Háromról is tanultunk a JDK 8-ban és a JDK 11-ben bevezetett új módszerek, amelyek megkönnyítik életünket.

Ezeknek a példáknak a kódja a GitHub webhelyen érhető el mag-java-11 a JDK 11 kivonatokhoz és core-java-regex a többieknek.