سؤال

لقد تعلمت للتو عن فئة Java's Scanner والآن أتساءل كيف تقارن/تتنافس مع StringTokenizer وString.Split.أعلم أن StringTokenizer وString.Split يعملان فقط على السلاسل، فلماذا أرغب في استخدام الماسح الضوئي لسلسلة؟هل المقصود من الماسح الضوئي هو أن يكون مركز تسوق متكامل للتقسيم؟

هل كانت مفيدة؟

المحلول

إنهم في الأساس خيول للدورات التدريبية.

  • Scanner تم تصميمه للحالات التي تحتاج فيها إلى تحليل سلسلة وسحب بيانات من أنواع مختلفة.إنها مرنة للغاية، ولكن يمكن القول إنها لا تمنحك أبسط واجهة برمجة تطبيقات (API) لمجرد الحصول على مجموعة من السلاسل المحددة بتعبير معين.
  • String.split() و Pattern.split() يمنحك بناء جملة سهلًا للقيام بالأخير، ولكن هذا هو كل ما يفعلونه في الأساس.إذا كنت تريد تحليل السلاسل الناتجة، أو تغيير المحدد في منتصف الطريق اعتمادًا على رمز مميز معين، فلن يساعدك ذلك في ذلك.
  • StringTokenizer بل هو أكثر تقييدا ​​من String.split(), ، وأيضًا أكثر عبثًا في الاستخدام.إنه مصمم بشكل أساسي لسحب الرموز المميزة بسلاسل فرعية ثابتة.وبسبب هذا القيد، فهو أسرع بمرتين تقريبًا String.split().(انظر بلدي مقارنة String.split() و StringTokenizer.) كما أنه يسبق واجهة برمجة تطبيقات التعبيرات العادية، منها String.split() هو جزء.

ستلاحظ من توقيتاتي ذلك String.split() لا يزال بإمكانه الرمز المميز آلاف السلاسل في بضعة أجزاء من الثانية على آلة نموذجية.وبالإضافة إلى ذلك، لديها ميزة أكثر StringTokenizer أنه يمنحك الإخراج كمصفوفة سلسلة، وهو ما تريده عادةً.باستخدام Enumeration, ، كما هو منصوص عليه StringTokenizer, ، "صعب الإرضاء من الناحية النحوية" في معظم الأوقات.من وجهة النظر هذه، StringTokenizer يعد هذا مضيعة للمساحة في الوقت الحاضر، ويمكنك أيضًا استخدامه فقط String.split().

نصائح أخرى

لنبدأ بالقضاء StringTokenizer. إنها قديمة ولا تدعم حتى التعبيرات العادية. تنص وثائقها:

StringTokenizer هو فئة قديمة يتم الاحتفاظ بها لأسباب توافق على الرغم من أن استخدامه قد تم تثبيته في رمز جديد. يوصى بأن يستخدم أي شخص يبحث عن هذه الوظيفة split طريقة String أو ال java.util.regex حزمة بدلا من ذلك.

لذلك دعونا نرميها على الفور. أن يترك split() و Scanner. ما الفرق بينهما؟

لأجل شئ واحد، split() ببساطة إرجاع صفيف ، مما يجعل من السهل استخدام حلقة foreach:

for (String token : input.split("\\s+") { ... }

Scanner تم تصميمه أشبه بالتيار:

while (myScanner.hasNext()) {
    String token = myScanner.next();
    ...
}

أو

while (myScanner.hasNextDouble()) {
    double token = myScanner.nextDouble();
    ...
}

(لديها بالأحرى واجهة برمجة تطبيقات كبيرة, ، لذلك لا تعتقد أنه يقتصر دائمًا على مثل هذه الأشياء البسيطة.)

يمكن أن تكون هذه الواجهة على غرار الدفق مفيدة لتحليل الملفات النصية البسيطة أو إدخال وحدة التحكم ، عندما لا يكون لديك (أو لا يمكنك الحصول عليها) جميع المدخلات قبل البدء في التحليل.

شخصيا ، المرة الوحيدة التي يمكنني تذكر استخدامها Scanner هو للمشاريع المدرسية ، عندما اضطررت إلى الحصول على مدخلات المستخدم من سطر الأوامر. يجعل هذا النوع من العملية سهلة. ولكن إذا كان لدي String أنني أريد الانقسام ، إنه أمر لا يوجد عقلانية تقريبًا split().

كان هناك دائما هناك. إنها الأسرع على الإطلاق ، لكن المصطلح الشبيه بالتعداد قد لا يبدو أنيقًا مثل الآخرين.

جاء الانقسام إلى الوجود على JDK 1.4. أبطأ من Tokenizer ولكن أسهل في الاستخدام ، لأنه قابل للاتصال من فئة السلسلة.

جاء الماسح الضوئي على JDK 1.5. إنه الأكثر مرونة ويملأ فجوة طويلة الأمد على واجهة برمجة تطبيقات Java لدعم ما يعادل عائلة وظيفة CS ScanF الشهيرة.

الانقسام بطيء ، ولكن ليس بطيئا مثل الماسح الضوئي. StringTokenizer أسرع من الانقسام. ومع ذلك ، وجدت أنه يمكنني الحصول على ضعف السرعة ، عن طريق تداول بعض المرونة ، للحصول على زيادة السرعة ، وهو ما فعلته في Jfastparser https://github.com/hughperkins/jfastparser

اختبار على سلسلة تحتوي على مليون زوجي:

Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms

إذا كان لديك كائن سلسلة تريد أن تميزه ، فابحث عن استخدام السلسلة ينقسم طريقة على stringtokenizer. إذا كنت تقوم بتحليل البيانات النصية من مصدر خارج البرنامج ، مثل من ملف ، أو من المستخدم ، فهذا هو المكان الذي يكون فيه الماسح الضوئي في متناول يدي.

string.split يبدو أن أبطأ بكثير من StringTokenizer. الميزة الوحيدة مع الانقسام هي أنك تحصل على مجموعة من الرموز. كما يمكنك استخدام أي تعبيرات منتظمة في الانقسام. org.apache.commons.lang.StringUtils لديه طريقة تقسيم تعمل بشكل أسرع بكثير من أي من اثنين بمعنى. StringTokenizer أو String.split. لكن استخدام وحدة المعالجة المركزية لجميع الثلاثة هو نفسه تقريبا. لذلك نحتاج أيضًا إلى طريقة أقل كثافة في وحدة المعالجة المركزية ، والتي ما زلت غير قادر على العثور عليها.

لقد أجريت مؤخرًا بعض التجارب حول الأداء السيئ لـ string.split () في المواقف الحساسة للغاية للأداء. قد تجد هذا مفيدًا.

http://eblog.chrononsystems.com/hidden-evils-of-javastringsplit-and-stringr

GIST هو أن string.split () يجمع نمط تعبير منتظم في كل مرة ، وبالتالي يمكن أن يبطئ البرنامج ، مقارنةً بما إذا كنت تستخدم كائن نمط مسبق واستخدامه مباشرة للعمل على سلسلة.

بالنسبة للسيناريوهات الافتراضية ، أود أن أقترح نمطًا.

public static ArrayList<String> splitBySingleChar(final char[] s,
        final char splitChar) {
    final ArrayList<String> result = new ArrayList<String>();
    final int length = s.length;
    int offset = 0;
    int count = 0;
    for (int i = 0; i < length; i++) {
        if (s[i] == splitChar) {
            if (count > 0) {
                result.add(new String(s, offset, count));
            }
            offset = i + 1;
            count = 0;
        } else {
            count++;
        }
    }
    if (count > 0) {
        result.add(new String(s, offset, count));
    }
    return result;
}

استخدم "ABC". tochararray () للحصول على صفيف char لسلسلة. فمثلا:

String s = "     a bb   ccc  dddd eeeee  ffffff    ggggggg ";
ArrayList<String> result = splitBySingleChar(s.toCharArray(), ' ');

أحد الاختلافات المهمة هو أن كلا من string.split () والماسح الضوئي يمكن أن ينتجا سلاسل فارغة ولكن stringtokenizer لا يفعل ذلك أبدًا.

فمثلا:

String str = "ab cd  ef";

StringTokenizer st = new StringTokenizer(str, " ");
for (int i = 0; st.hasMoreTokens(); i++) System.out.println("#" + i + ": " + st.nextToken());

String[] split = str.split(" ");
for (int i = 0; i < split.length; i++) System.out.println("#" + i + ": " + split[i]);

Scanner sc = new Scanner(str).useDelimiter(" ");
for (int i = 0; sc.hasNext(); i++) System.out.println("#" + i + ": " + sc.next());

انتاج:

//StringTokenizer
#0: ab
#1: cd
#2: ef
//String.split()
#0: ab
#1: cd
#2: 
#3: ef
//Scanner
#0: ab
#1: cd
#2: 
#3: ef

وذلك لأن المحدد لـ string.split () و Scanner.usedElimiter () ليس مجرد سلسلة ، ولكنه تعبير منتظم. يمكننا استبدال المحدد "" +"في المثال أعلاه لجعلها تتصرف مثل StringTokenizer.

تعمل String.Split () بشكل جيد للغاية ولكن لها حدودها الخاصة ، كما لو كنت تريد تقسيم سلسلة كما هو موضح أدناه استنادًا إلى رمز أنبوب واحد أو مزدوج (|) ، فهو لا يعمل. في هذه الحالة ، يمكنك استخدام StringTokenizer.

ABC | IJK

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top