سؤال

بتشجيع من هذه, ، وحقيقة أن لدي مليارات من السلسلة لتحليلها، حاولت تعديل التعليمات البرمجية الخاصة بي stringtokenizer. بدلاً من سلسلة[

الشيء الوحيد المتبقي بيني والحصول على تعزيز أداء X2 لذيذ هو حقيقة أنه عندما تفعل

"dog,,cat".split(",")
//output: ["dog","","cat"]

StringTokenizer("dog,,cat")
// nextToken() = "dog"
// nextToken() = "cat"

كيف يمكنني تحقيق نتائج مماثلة مع stringtokenizer؟ هل هناك طرق أسرع للقيام بذلك؟

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

المحلول

هل أنت فقط تتكامل في الفواصل؟ إذا كان الأمر كذلك، فسوف أكتب خصمها الخاص - قد ينتهي الأمر بكونه أكثر كفاءة من StringTokenizer أكثر من الأغراض العامة التي يمكن أن تبحث عن رموز متعددة، ويمكنك أن تتصرف بذلك. لمثل هذه الحالة الاستخدام البسيطة، يمكن أن يكون تطبيق بسيط.

إذا كان من المفيد، فيمكنك التنفيذ Iterable<String> والحصول على دعم محسن لمدة حلقة مع الكتابة القوية بدلا من Enumeration الدعم المقدم من قبل StringTokenizer. وبعد اسمحوا لي أن أعرف إذا كنت تريد أي مساعدة ترميز مثل هذا الوحش - لا ينبغي أن يكون صعبا للغاية.

بالإضافة إلى ذلك، سأحاول تشغيل اختبارات الأداء على بياناتك الفعلية قبل القفز بعيدا عن الحل الحالي. هل لديك أي فكرة عن مقدار وقت التنفيذ الخاص بك فعلا أمضى في String.splitب أعلم أن لديك الكثير من السلاسل لتحليلها، ولكن إذا كنت تفعل أي شيء مهم معهم بعد ذلك، فإنني أتوقع أن يكون أكثر أهمية بكثير من الانقسام.

نصائح أخرى

بعد العبث مع StringTokenizer فئة، لم أجد طريقة لإرضاء المتطلبات للعودة ["dog", "", "cat"].

علاوة على ذلك، و StringTokenizer يتم ترك الطبقة فقط لأسباب التوافق، واستخدام String.split هو مغرور. من مواصفات API ل StringTokenizer:

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

منذ القضية هي الأداء الضعيف المفترض String.split الطريقة، نحن بحاجة إلى العثور على بديل.

ملاحظة: أقول "أداء ضعيف يفترض" لأنه من الصعب تحديد أن كل حالة استخدام ستؤدي إلى StringTokenizer أن تكون متفوقة على String.split طريقة. علاوة على ذلك، في كثير من الحالات، ما لم تكن خصم السلاسل بالفعل عنق الزجاجة للتطبيق الذي يحدده التنميط السليم، أشعر أنه سينتهي الأمر بكونه الأمثل المبكر، إذا كان أي شيء. سأمبل أن أقول رمز الكتابة ذات مغزى وسهل الفهم قبل مغامرة على التحسين.

الآن، من المتطلبات الحالية، من المحتمل أن تتدحرج ممتلكاتنا الخاصة لن تكون صعبة للغاية.

لفة tokenzier الخاصة بنا!

ما يلي هو مثمر مملز بسيط كتبته. يجب أن ألاحظ أنه لا توجد تحسينات السرعة، ولا توجد شيكات خطأ لمنع الذهاب في نهاية السلسلة - وهذا تنفيذ سريع وقذر:

class MyTokenizer implements Iterable<String>, Iterator<String> {
  String delim = ",";
  String s;
  int curIndex = 0;
  int nextIndex = 0;
  boolean nextIsLastToken = false;

  public MyTokenizer(String s, String delim) {
    this.s = s;
    this.delim = delim;
  }

  public Iterator<String> iterator() {
    return this;
  }

  public boolean hasNext() {
    nextIndex = s.indexOf(delim, curIndex);

    if (nextIsLastToken)
      return false;

    if (nextIndex == -1)
      nextIsLastToken = true;

    return true;
  }

  public String next() {
    if (nextIndex == -1)
      nextIndex = s.length();

    String token = s.substring(curIndex, nextIndex);
    curIndex = nextIndex + 1;

    return token;
  }

  public void remove() {
    throw new UnsupportedOperationException();
  }
}

ال MyTokenizer سوف يستغرق String لتكييف و String كمسؤول، واستخدام String.indexOf طريقة لتنفيذ البحث عن محددات. يتم إنتاج الرموز من قبل String.substring طريقة.

أود أن أشك في أن هناك بعض التحسينات الأداء من خلال العمل على السلسلة في char[] مستوى بدلا من في String مستوى. لكنني سأترك ذلك بمثابة تمرين للقارئ.

فئة تنفذ أيضا Iterable و Iterator من أجل الاستفادة من for-each بناء الحلقة التي تم تقديمها في جافا 5. StringTokenizer هو Enumerator, ، ولا يدعم for-each بناء.

هل هو أسرع؟

من أجل معرفة ما إذا كان هذا أسرع، كتبت برنامجا لمقارنة السرعات بالطرق الأربعة التالية:

  1. استخدام StringTokenizer.
  2. استخدام الجديد MyTokenizer.
  3. استخدام String.split.
  4. استخدام التعبير العادي المعاد Pattern.compile.

في الأساليب الأربعة، السلسلة "dog,,cat" تم فصله إلى الرموز. على الرغم من أن StringTokenizer يتم تضمينها في المقارنة، تجدر الإشارة إلى أنه لن يرجع النتيجة المرجوة لل ["dog", "", "cat].

تم تكرار التزخم لمدة 1 مليون مرة لإعطاء ما يكفي من الوقت لإشعار الفرق في الأساليب.

كان التعليمات البرمجية المستخدمة في المعيار البسيط ما يلي:

long st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
  StringTokenizer t = new StringTokenizer("dog,,cat", ",");
  while (t.hasMoreTokens()) {
    t.nextToken();
  }
}
System.out.println(System.currentTimeMillis() - st);

st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
  MyTokenizer mt = new MyTokenizer("dog,,cat", ",");
  for (String t : mt) {
  }
}
System.out.println(System.currentTimeMillis() - st);

st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
  String[] tokens = "dog,,cat".split(",");
  for (String t : tokens) {
  }
}
System.out.println(System.currentTimeMillis() - st);

st = System.currentTimeMillis();
Pattern p = Pattern.compile(",");
for (int i = 0; i < 1e6; i++) {
  String[] tokens = p.split("dog,,cat");
  for (String t : tokens) {
  }
}
System.out.println(System.currentTimeMillis() - st);

النتائج

تم تشغيل الاختبارات باستخدام Java SE 6 (بناء 1.6.0_12-B04)، وكانت النتائج ما يلي:

 تشغيل 1 تشغيل 2 تشغيل 2 Run 3 Run 4 Run 5 ----- ----- ----- ----- ----- stringtokenizer 172 188 187 172 172 mytokenizer 234 234 234 234 235 string.split 1172 1156 1171 1172 1156 نمط.com 906 891 891 907 906

لذلك، كما يمكن أن نرى من الاختبار المحدود وخمس فقط أشواط، StringTokenizer هل في الواقع يخرج الأسرع، ولكن MyTokenizer جاء في كمغلق 2nd. ثم، String.split كان أبطأ، وكان التعبير المنتظم العمود أسرع قليلا من split طريقة.

كما هو الحال مع أي معيار صغير، ربما لم يكن ممثلا للغاية لظروف الحياة الحقيقية، لذلك يجب أن تؤخذ النتائج مع الحبوب (أو تل) من الملح.

ملاحظة: بعد أن قام ببعض المعايير السريعة، يتحول الماسح الضوئي إلى أن تكون أبطأ أربع مرات من String.Split. وبالتالي، لا تستخدم الماسح الضوئي.

(سأترك النشر لأعلى لتسجيل حقيقة أن الماسح الضوئي فكرة سيئة في هذه الحالة. (اقرأ كما: لا تقم الهبوط لي لاقتراح الماسح الضوئي، يرجى ...))

على افتراض أنك تستخدم Java 1.5 أو أعلى، حاول الماسح الضوئي, ، والتي تنفذ Iterator<String>, ، كما يحدث:

Scanner sc = new Scanner("dog,,cat");
sc.useDelimiter(",");
while (sc.hasNext()) {
    System.out.println(sc.next());
}

يعطي

dog

cat

اعتمادا على نوع السلاسل التي تحتاج إلى تتكاملها، يمكنك كتابة الفاصل الخاص بك بناء على String.indexof () على سبيل المثال. يمكنك أيضا إنشاء حل متعدد الأساس لتحسين الأداء أكثر من ذلك، حيث أن خصوم السلاسل مستقل عن بعضها البعض. العمل على دفعات القول - 100 سلاسل لكل كور. هل string.split () أو Water آخر.

بدلا من stringtokenizer، يمكنك تجربة فئة Strtokenizer من Apache Commons Lang، التي اقتبسها:

يمكن أن تقسم هذه الفئة سلسلة في العديد من السلاسل الأصغر. يهدف إلى القيام بعمل مماثل ل Stringtokenizer، ومع ذلك، فإنه يوفر المزيد من السيطرة والمرونة بما في ذلك تنفيذ واجهة List Listitorator.

قد تتم إزالة الرموز الفارغة أو إرجاعها ك NULL.

هذا يبدو وكأنه ما تحتاجه، وأعتقد؟

يمكنك أن تفعل شيئا من هذا القبيل. انها ليست مثالية، ولكن قد تعمل من أجلك.

public static List<String> find(String test, char c) {
    List<String> list = new Vector<String>();
    start;
    int i=0;
    while (i<=test.length()) {
        int start = i;
        while (i<test.length() && test.charAt(i)!=c) {
            i++;
        }
        list.add(test.substring(start, i));
        i++;
    }
    return list;
}

إذا كان ذلك ممكنا، فيمكنك Ommit على قائمة القائمة وأعلم شيئا مباشرة إلى Substring:

public static void split(String test, char c) {
    int i=0;
    while (i<=test.length()) {
        int start = i;
        while (i<test.length() && test.charAt(i)!=c) {
            i++;
        }
        String s = test.substring(start,i);
         // do something with the string here
        i++;
    }
}

على نظامي، الطريقة الأخيرة أسرع من الحل stringtokenizer، ولكن قد ترغب في اختبار كيفية عملك. (بالطبع يمكنك جعل هذه الطريقة أقصر قليلا من خلال ommite {} في الثانية الثانية أثناء البحث وبالطبع يمكنك استخدام حلقة واحدة بدلا من الحلقة الخارجية، بما في ذلك أخيرا I ++ في ذلك، لكنني لم أفعل ذلك أفعل ذلك هنا لأنني أعتبر أن النمط السيئ.

حسنا، أسرع شيء يمكنك القيام به هو اجتياز السلسلة يدويا، على سبيل المثال

List<String> split(String s) {
        List<String> out= new ArrayList<String>();
           int idx = 0;
           int next = 0;
        while ( (next = s.indexOf( ',', idx )) > -1 ) {
            out.add( s.substring( idx, next ) );
            idx = next + 1;
        }
        if ( idx < s.length() ) {
            out.add( s.substring( idx ) );
        }
               return out;
    }

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

في النهاية، ربما لا يستحق عناء.

أود أن أوصي جوجا جوجل Splitter.
قارنت ذلك كوه اختبار وحصلت على النتائج التالية:

Stringtokenizer 104.
Google Guava Splitter 142
string.split 446.
Regexp 299.

إذا تم تنظيم إدخالك، فيمكنك إلقاء نظرة على محمول Javacc. يولد فئة Java يقرأ المدخلات الخاصة بك. انها تبدو مثل هذا:

TOKEN { <CAT: "cat"> , <DOG:"gog"> }

input: (cat() | dog())*


cat: <CAT>
   {
   animals.add(new Animal("Cat"));
   }

dog: <DOG>
   {
   animals.add(new Animal("Dog"));
   }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top