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:
- Van egy listánk 5 000 000 számmal 1-től 5 000 000-ig
- 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.