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:
- Imamo popis s 5.000.000 brojeva od 1 do 5.000.000
- 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.