Pra-menyusun Pola Regex Ke Objek Corak

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan melihat faedah pra-menyusun corak regex dan kaedah baru yang diperkenalkan di Java 8 dan 11 .

Ini bukan cara regex, tetapi kami mempunyai API Panduan Untuk Ekspresi Biasa Java untuk tujuan tersebut.

2. Faedah

Penggunaan semula pasti akan membawa peningkatan prestasi, kerana kita tidak perlu membuat dan mencipta semula objek yang sama dari waktu ke waktu. Oleh itu, kita boleh menganggap bahawa penggunaan semula dan prestasi sering dikaitkan.

Mari kita lihat prinsip ini kerana berkaitan dengan penyusunan Corak #. W e'll menggunakan penanda aras yang mudah :

  1. Kami mempunyai senarai dengan 5,000,000 nombor dari 1 hingga 5,000,000
  2. Regex kami akan sepadan dengan nombor genap

Oleh itu, mari kita uji menguraikan nombor-nombor ini dengan ungkapan regex Java berikut:

  • String.matches (regex)
  • Pattern.matches (regex, charSequence)
  • Pattern.compile (regex) .matcher (charSequence) .matches ()
  • Regex pra-disusun dengan banyak panggilan ke preCompiledPattern.matcher (value) .matches ()
  • Regex pra-disusun dengan satu contoh Matcher dan banyak panggilan ke matcherFromPreCompiledPattern.reset (nilai) .matches ()

Sebenarnya, jika kita melihat pelaksanaan String # match :

public boolean matches(String regex) { return Pattern.matches(regex, this); }

Dan pada perlawanan Pola # :

public static boolean matches(String regex, CharSequence input) { Pattern p = compile(regex); Matcher m = p.matcher(input); return m.matches(); }

Kemudian, kita dapat membayangkan bahawa tiga ungkapan pertama akan menunjukkan prestasi yang sama. Ini kerana ungkapan pertama memanggil yang kedua, dan yang kedua memanggil yang ketiga.

Perkara kedua ialah kaedah ini tidak menggunakan semula corak Pola dan Pencocok yang dibuat. Dan, seperti yang akan kita lihat di penanda aras, ini menurunkan prestasi dengan faktor enam :

 @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) { Instant start = Instant.now(); for (String value : values) { bh.consume(value.matches(PATTERN)); } } 

Melihat hasil penanda aras, tidak ada keraguan bahawa Pola yang telah disusun sebelumnya dan Matcher yang digunakan semula adalah pemenang dengan hasil lebih dari enam kali lebih cepat :

Benchmark Mode Cnt Score Error Units 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

Di luar waktu persembahan, kami juga mempunyai bilangan objek yang dibuat :

  • Tiga bentuk pertama:
    • 5,000,000 Contoh corak dibuat
    • 5,000,000 contoh Matcher dibuat
  • preCompiledPattern.matcher (nilai). padanan ()
    • 1 Corak contoh dibuat
    • 5,000,000 contoh Matcher dibuat
  • matcherFromPreCompiledPattern.reset (nilai). padanan ()
    • 1 Corak contoh dibuat
    • 1 contoh Matcher dibuat

Oleh itu, bukannya mewakilkan regex kami ke pertandingan String # atau Pattern # match yang selalu akan membuat corak dan Matcher . Kita harus menyusun semula regex kita untuk memperoleh prestasi dan menghasilkan lebih sedikit objek.

Untuk mengetahui lebih lanjut mengenai prestasi dalam regex, lihat Gambaran Keseluruhan Prestasi Ekspresi Biasa di Java.

3. Kaedah Baru

Sejak pengenalan antara muka dan aliran berfungsi, penggunaan semula menjadi lebih mudah.

The Corak kelas telah berkembang dalam versi Java baru untuk menyediakan integrasi dengan sungai dan lambdas.

3.1. Jawa 8

Java 8 memperkenalkan dua kaedah baru: splitAsStream dan asPredicate .

Mari lihat beberapa kod untuk splitAsStream yang membuat aliran dari urutan input yang diberikan di sekitar padanan corak:

@Test public void givenPreCompiledPattern_whenCallSplitAsStream_thenReturnArraySplitByThePattern() { Pattern splitPreCompiledPattern = Pattern.compile("__"); Stream textSplitAsStream = splitPreCompiledPattern.splitAsStream("My_Name__is__Fabio_Silva"); String[] textSplit = textSplitAsStream.toArray(String[]::new); assertEquals("My_Name", textSplit[0]); assertEquals("is", textSplit[1]); assertEquals("Fabio_Silva", textSplit[2]); }

The asPredicate kaedah mencipta predikat yang berkelakuan seolah-olah ia mewujudkan matcher dari urutan input dan kemudian panggilan mencari:

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

Mari buat corak yang sesuai dengan nama dari senarai yang mempunyai sekurang-kurangnya nama pertama dan terakhir dengan masing-masing sekurang-kurangnya tiga huruf:

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

3.2. Jawa 11

Java 11 memperkenalkan kaedah asMatchPredicate yang membuat predikat yang berkelakuan seolah-olah membuat pemadan dari urutan input dan kemudian memanggil pertandingan:

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

Mari buat corak yang sesuai dengan nama dari senarai yang hanya mempunyai nama depan dan belakang dengan sekurang-kurangnya tiga huruf:

@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. Kesimpulan

Dalam tutorial ini, kami melihat bahawa penggunaan corak pra-kompilasi membawa kami prestasi yang jauh lebih unggul .

Kami juga mempelajari tiga kaedah baru yang diperkenalkan dalam JDK 8 dan JDK 11 yang menjadikan kehidupan kita lebih mudah .

Kod untuk contoh ini boleh didapati di GitHub dalam core-java-11 untuk coretan JDK 11 dan core-java-regex untuk yang lain.