הידור מראש דפוסי Regex לחפצי תבנית

1. סקירה כללית

במדריך זה נראה את היתרונות של הידור מראש של דפוס regex וה שיטות חדשות שהוצגו ב- Java 8 ו- 11.

זה לא יהיה הוראות הדרכה regex, אבל יש לנו מדריך מעולה ל- Java Regular Expressions API למטרה זו.

2. יתרונות

שימוש חוזר מביא בהכרח בעלייה בביצועים, מכיוון שאיננו צריכים ליצור ולשחזר מופעים של אותם אובייקטים פעם אחר פעם. לכן, אנו יכולים להניח כי שימוש חוזר וביצועים קשורים לעיתים קרובות.

בואו נסתכל על עיקרון זה כפי שהוא נוגע אליו תבנית # הידור. Wתשתמש במדד פשוט:

  1. יש לנו רשימה עם 5,000,000 מספרים מ -1 עד 5,000,000
  2. ה- regex שלנו יתאים למספרים זוגיים

אז בואו נבחן את הניתוח של המספרים הללו בביטויים הבאים של Java regex:

  • String.matches (regex)
  • דפוס. התאמות (regex, charSequence)
  • Pattern.compile (regex) .matcher (charSequence) .matches ()
  • רגקס מורכב מראש עם שיחות רבות אל preCompiledPattern.matcher (value) .matches ()
  • רכיב regex שהורכב מראש עם אחד שידוך מופע ושיחות רבות ל matcherFromPreCompiledPattern.reset (value) .matches ()

למעשה, אם נסתכל על מחרוזת # גפרוריםיישום:

התאמות בוליאניות ציבוריות (מחרוזת regex) {return Pattern.matches (regex, זה); }

וב- דפוסי # גפרורים:

התאמות בוליאניות סטטיות ציבוריות (מחרוזת regex, קלט CharSequence) {תבנית p = הידור (regex); התאמה m = עמ 'התאמה (קלט); החזר m.matches (); }

ואז נוכל לדמיין זאת שלושת הביטויים הראשונים יופיעו באופן דומה. הסיבה לכך היא שהביטוי הראשון מכנה את השני, והשני קורא את השלישי.

הנקודה השנייה היא ששיטות אלה אינן עושות שימוש חוזר ב- תבנית ו שידוך מקרים שנוצרו. וכפי שנראה במדד, זה משפיל את הביצועים בגורם שש:

 @Benchmark matcher ריק ריק .FromPreCompiledPatternResetMatches (Blackhole bh) {עבור (ערך מחרוזת: ערכים) {bh.consume (matcherFromPreCompiledPattern.reset (value) .matches ()); }} @Benchmark public void preCompiledPatternMatcherMatches (Blackhole bh) {for (ערך מחרוזת: ערכים) {bh.consume (preCompiledPattern.matcher (value) .matches ()); }} @Benchmark תבנית חלל ציבוריתCompileMatcherMatches (Blackhole bh) {עבור (ערך מחרוזת: ערכים) {bh.consume (Pattern.compile (PATTERN) .matcher (value) .matches ()); }} @Benchmark ריקים ציבוריים PatternMatches (Blackhole bh) {עבור (ערך מחרוזת: ערכים) {bh.consume (Pattern.matches (PATTERN, value)); }} @Benchmark public void stringMatchs (Blackhole bh) {Instant start = Instant.now (); עבור (ערך מחרוזת: ערכים) {bh.consume (value.matches (PATTERN)); }} 

אם מסתכלים על תוצאות המבחן, אין ספק שזה נערך מראש תבנית ושימוש חוזר שידוך הם המנצחים בתוצאה המהירה פי שישה:

בנצ'מרק מצב CNT ציון שגיאה יחידות PatternPerformanceComparison.matcherFromPreCompiledPatternResetMatches avgt 20 278.732 ± 22.960 ms / 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 תבנית ביצועים Comparison.pattern התאמות ממוצע 20 1792.874 ± 130.213 ms / op

מעבר לזמני הביצוע, יש לנו גם את מספר האובייקטים שנוצרו:

  • שלוש הצורות הראשונות:
    • 5,000,000 תבנית מקרים שנוצרו
    • 5,000,000 שידוך מקרים שנוצרו
  • preCompiledPattern.matcher (value) .matches ()
    • 1 תבנית מופע נוצר
    • 5,000,000 שידוך מקרים שנוצרו
  • matcherFromPreCompiledPattern.reset (value) .matches ()
    • 1 תבנית מופע נוצר
    • 1 שידוך מופע נוצר

אז במקום להאציל את ה- regex שלנו ל- מחרוזת # גפרורים אוֹ תבניות # תבניות שתמיד ייצור את תבנית ו שידוך מקרים. עלינו להרכיב את ה- regex שלנו מראש כדי להרוויח ביצועים ויש לו פחות אובייקטים שנוצרו.

למידע נוסף על ביצועים ב- regex עיין בסקירה הכללית של ביצועי הביטויים הרגולריים ב- Java.

3. שיטות חדשות

מאז הכנסת ממשקים וזרמים פונקציונליים, השימוש החוזר נעשה קל יותר.

ה תבנית class התפתח בגרסאות Java חדשות לספק שילוב עם נחלים ולמבדות.

3.1. ג'אווה 8

Java 8 הציגה שתי שיטות חדשות: splitAsStream ו כפרדיט.

בואו נסתכל על איזה קוד עבור splitAsStream שיוצר זרם מרצף הקלט הנתון סביב התאמות של התבנית:

@Test הציבור בטל givenPreCompiledPattern_whenCallSplitAsStream_thenReturnArraySplitByThePattern () {תבנית splitPreCompiledPattern = Pattern.compile ("__"); הזרם textSplitAsStream = splitPreCompiledPattern.splitAsStream ("My_Name__is__Fabio_Silva"); מחרוזת [] textSplit = textSplitAsStream.toArray (מחרוזת [] :: חדש); assertEquals ("My_Name", textSplit [0]); assertEquals ("is", textSplit [1]); assertEquals ("Fabio_Silva", textSplit [2]); }

ה כפרדיט השיטה יוצרת פרדיקט שמתנהג כאילו הוא יוצר התאמה מרצף הקלט ואז השיחות מוצאות:

מחרוזת -> התאמה (מחרוזת) .find ();

בואו ניצור תבנית התואמת שמות מרשימה הכוללת לפחות שמות פרטיים ושמות משפחה עם לפחות שלוש אותיות כל אחת:

@Test public void givenPreCompiledPattern_whenCallAsPredicate_thenReturnPredicateToFindPatternInTheList () {List namesToValidate = Arrays.asList ("פאביו סילבה", "מר סילבה"); תבנית firstLastNamePreCompiledPattern = Pattern.compile ("[a-zA-Z] {3,} [a-zA-Z] {3,}"); Predicate patternsAsPredicate = firstLastNamePreCompiledPattern.asPredicate (); רשימת validNames = namesToValidate.stream () .filter (patternsAsPredicate) .collect (Collectors.toList ()); assertEquals (1, validNames.size ()); assertTrue (validNames.contains ("פאביו סילבה")); }

3.2. ג'אווה 11

ג'אווה 11 הציגה את asMatchPredicate שיטה שיוצר פרדיקט שמתנהג כאילו הוא יוצר התאמה מרצף הקלט ואז קורא התאמות:

מחרוזת -> התאמה (מחרוזת) .matches ();

בואו ניצור תבנית שתואמת שמות מרשימה שיש בה רק שם פרטי ושם משפחה עם לפחות שלוש אותיות כל אחת:

@Test הציבור בטל givenPreCompiledPattern_whenCallAsMatchPredicate_thenReturnMatchPredicateToMatchesPattern () {List namesToValidate = Arrays.asList ("פאביו סילבה", "פאביו לואיס סילבה"); תבנית firstLastNamePreCompiledPattern = Pattern.compile ("[a-zA-Z] {3,} [a-zA-Z] {3,}"); Predicate patternAsMatchPredicate = firstLastNamePreCompiledPattern.asMatchPredicate (); רשימת validatedNames = namesToValidate.stream () .filter (patternAsMatchPredicate) .collect (Collectors.toList ()); assertTrue (validatedNames.contains ("פאביו סילבה")); assertFalse (validatedNames.contains ("פאביו לואיס סילבה")); }

4. מסקנה

במדריך זה ראינו כי ה- השימוש בדפוסים שהורכבו מראש מביא לנו ביצועים עדיפים בהרבה.

למדנו גם על שלוש שיטות חדשות שהוצגו ב- JDK 8 ו- JDK 11 המקלות על חיינו.

הקוד עבור דוגמאות אלה זמין ב- GitHub ב core-java-11 עבור קטעי ה- JDK 11 ו- core-java-regex עבור האחרים.


$config[zx-auto] not found$config[zx-overlay] not found