Predkompajlirajte Regex uzorke u predmete uzoraka

1. Pregled

U ovom uputstvu vidjet ćemo blagodati predkompajliranja uzorka regularnih izraza i nove metode uvedene u Javi 8 i 11.

Ovo neće biti redoviti izraz, ali u tu svrhu imamo izvrsni API za Vodič kroz Java Regular Expressions API.

2. Prednosti

Ponovna upotreba neizbježno donosi poboljšanje performansi, jer ne trebamo stvarati i ponovno stvarati instance istih objekata s vremena na vrijeme. Dakle, možemo pretpostaviti da su ponovna upotreba i izvedba često povezani.

Pogledajmo ovaj princip kakav se odnosi na njega Uzorak # sastaviti. WKoristit ću jednostavno mjerilo:

  1. Imamo popis s 5.000.000 brojeva od 1 do 5.000.000
  2. Naš će se regularni izraz podudarati s parnim brojevima

Dakle, testirajmo raščlanjivanje ovih brojeva sa sljedećim izrazima Java regularnih izraza:

  • String.matches (regex)
  • Pattern.matches (regularni izraz, charSequence)
  • Pattern.compile (regularni izraz) .matcher (charSequence) .matches ()
  • Unaprijed sastavljeni regularni izraz s mnogo poziva na preCompiledPattern.matcher (vrijednost) .matches ()
  • Unaprijed sastavljeni regularni izraz s jednim Podudaranje instance i mnogi pozivi na matcherFromPreCompiledPattern.reset (value) .matches ()

Zapravo, ako pogledamo Niz # podudaranjaImplementacija:

javna logička podudaranja (String regex) {return Pattern.matches (regex, this); }

I u Uzorak # odgovara:

javna statička logička podudaranja (String regex, CharSequence input) {Uzorak p = kompajliranje (regex); Podudaranje m = p.matcher (ulaz); vratiti m.matches (); }

Tada to možemo zamisliti prva tri izraza izvest će slično. To je zato što prvi izraz naziva drugi, a drugi treći.

Druga je stvar da ove metode ne koriste ponovno Uzorak i Podudaranje stvorene instance. I, kao što ćemo vidjeti u referentnoj vrijednosti, to pogoršava performanse šest puta:

 @Benchmark javna void matcherFromPreCompiledPatternResetMatches (Blackhole bh) {for (String value: values) {bh.consume (matcherFromPreCompiledPattern.reset (value) .matches ()); }} @Benchmark javna praznina preCompiledPatternMatcherMatches (Blackhole bh) {for (Vrijednost niza: vrijednosti) {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 javna void patternMatches (Blackhole bh) {for (Vrijednost niza: vrijednosti) {bh.consume (Pattern.matches (PATTERN, value)); }} @Benchmark javna void stringMatchs (Blackhole bh) {Instant start = Instant.now (); za (Vrijednost niza: vrijednosti) {bh.consume (value.matches (PATTERN)); }} 

Gledajući referentne rezultate, to nema sumnje unaprijed sastavljen Uzorak i ponovno upotrijebljen Podudaranje su pobjednici s rezultatom više od šest puta bržim:

PatternPerformanceComparison.matcherFromPreCompiledPatternResetMatches jedinice mjerilo način CNT rezultat pogrešaka 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 prosj. 20 1792.874 ± 130.213 ms / op

Osim vremena izvedbe, imamo i broj stvorenih objekata:

  • Prva tri oblika:
    • 5,000,000 Uzorak stvorene instance
    • 5,000,000 Podudaranje stvorene instance
  • preCompiledPattern.matcher (vrijednost) .matches ()
    • 1 Uzorak instanca stvorena
    • 5,000,000 Podudaranje stvorene instance
  • matcherFromPreCompiledPattern.reset (value) .matches ()
    • 1 Uzorak instanca stvorena
    • 1 Podudaranje instanca stvorena

Dakle, umjesto da delegiramo naš regularni izraz na Niz # podudaranja ili Uzorak # odgovara koji će uvijek stvoriti Uzorak i Podudaranje instance. Trebali bismo prethodno sastaviti svoj regularni izraz kako bismo zaradili izvedbu i imamo manje stvorenih objekata.

Da biste saznali više o izvedbi u regularnom izrazu, pogledajte naš Pregled izvedbe regularnih izraza na Javi.

3. Nove metode

Od uvođenja funkcionalnih sučelja i strujanja, ponovna upotreba postala je lakša.

The Uzorak klasa evoluirala je u novim Java verzijama kako bi se osigurala integracija s potocima i lambdama.

3.1. Java 8

Java 8 predstavila je dvije nove metode: splitAsStream i asPredicirati.

Pogledajmo neki kod za splitAsStream koji stvara tok iz zadanog ulaznog niza oko podudaranja uzorka:

@Test javna praznina givenPreCompiledPattern_whenCallSplitAsStream_thenReturnArraySplitByThePattern () {Pattern splitPreCompiledPattern = Pattern.compile ("__"); Struja textSplitAsStream = splitPreCompiledPattern.splitAsStream ("My_Name__is__Fabio_Silva"); String [] textSplit = textSplitAsStream.toArray (String [] :: novo); assertEquals ("Moje_ime", textSplit [0]); assertEquals ("je", textSplit [1]); assertEquals ("Fabio_Silva", textSplit [2]); }

The asPredicirati metoda stvara predikat koji se ponaša kao da stvara podudarnost iz ulazne sekvence, a zatim poziva find:

niz -> podudaranje (niz) .find ();

Stvorimo obrazac koji se podudara s imenima s popisa koji imaju barem imena i prezimena s najmanje tri slova:

@Test javna praznina givenPreCompiledPattern_whenCallAsPredicate_thenReturnPredicateToFindPatternInTheList () {List namesToValidate = Arrays.asList ("Fabio Silva", "Gospodin Silva"); Uzorak firstLastNamePreCompiledPattern = Pattern.compile ("[a-zA-Z] {3,} [a-zA-Z] {3,}"); Predikatni obrasciAsPredicate = firstLastNamePreCompiledPattern.asPredicate (); Popis validNames = namesToValidate.stream () .filter (patternAsPredicate) .collect (Collectors.toList ()); assertEquals (1, validNames.size ()); assertTrue (validNames.contens ("Fabio Silva")); }

3.2. Java 11

Java 11 predstavila je asMatchPredicate metoda koji stvara predikat koji se ponaša kao da stvara podudarnost iz ulazne sekvence, a zatim poziva podudaranja:

string -> podudaranje (niz) .matches ();

Stvorimo obrazac koji se podudara s imenima s popisa koji imaju samo ime i prezime s najmanje tri slova:

@Test javna praznina givenPreCompiledPattern_whenCallAsMatchPredicate_thenReturnMatchPredicateToMatchesPattern () {Imena imenaToValidate = Arrays.asList ("Fabio Silva", "Fabio Luis Silva"); Uzorak firstLastNamePreCompiledPattern = Pattern.compile ("[a-zA-Z] {3,} [a-zA-Z] {3,}"); Predikat patternAsMatchPredicate = firstLastNamePreCompiledPattern.asMatchPredicate (); Popis validatedNames = namesToValidate.stream () .filter (patternAsMatchPredicate) .collect (Collectors.toList ()); assertTrue (validatedNames.contens ("Fabio Silva")); assertFalse (validatedNames.contens ("Fabio Luis Silva")); }

4. Zaključak

U ovom uputstvu vidjeli smo da upotreba prethodno sastavljenih uzoraka donosi nam daleko bolju izvedbu.

Saznali smo i za troje nove metode uvedene u JDK 8 i JDK 11 koje nam olakšavaju život.

Kôd za ove primjere dostupan je na GitHub u jezgra-java-11 za isječke JDK 11 i jezgra-java-regularni izraz za ostale.