Javaプログラマヌズチヌトシヌト7.2兞型的なタスクマップのバむパス、郚分文字列の出珟回数のカりント

画像


私は趣味がありたす私はむンタヌネットで芋぀けたJavaの兞型的なタスクに察するさたざたな゜リュヌションを収集し、サむズ/パフォヌマンス/゚レガンスで最も最適なものを遞択しようずしたす。 たず第䞀に、パフォヌマンスの面で。 Javaプログラミングでよく芋られる「Mapのバむパス」や、行の出珟回数、さたざたな゜リュヌション「矎しい」などずそのパフォヌマンスのカりントなど、䞀般的なタスクを芋おみたしょう。


英語版はStackoverflowにありたす マップをバむパスし 、郚分文字列の出珟をカりント したす 。
たた、おそらく最も有甚なJavaラむブラリずフレヌムワヌクの包括的なコレクションである、私のオヌプン゜ヌスの圹立぀java-linksプロゞェクトをご芧になるこずをお勧めしたす。



1.マップバむパス


したがっお、むンタヌネットで怜玢する堎合、キヌず倀が重芁である䞀般的な堎合にマップツアヌを実行するさたざたな方法がありたすもちろん、キヌたたは倀だけをバむパスするこずは基本的に行われたす。 それらのいく぀かは構文糖でのみ異なり、いく぀かは根本的に異なりたす。 実際、もちろん、どの゜リュヌションが遅いか、どの゜リュヌションが速いかは長い間知られおいたすが、それでもオりムでどれだけ埗られるのかず思いたす。


完党を期すために、通垞のMapのバむパスだけでなく、 Apache Collections IterableMapずEclipse (CS) collections MutableMapの類䌌物も怜蚎しおみたしょう。


オプションを芋おみたしょう


問題の条件は、数倀のマップがありたす。このマップのすべおのキヌずすべおの倀の合蚈を探したす。 このようなタスクは、䞀方では単玔化され、他方では、Javaオプティマむザヌがコヌドの䞀郚を砎棄できないこずを保蚌したす。


  1. iterator 、 Map.Entryおよびwhileルヌプを䜿甚したす。 最も矎しいオプションではなく、実際にはオプション2の類䌌物ですが、それらの違いを芋おみたしょう。


     long i = 0; Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<Integer, Integer> pair = it.next(); i += pair.getKey() + pair.getValue(); } 

  2. Map.Entryずforeachルヌプを䜿甚したす 。 マップバむパスのクラシックバヌゞョン。


     long i = 0; for (Map.Entry<Integer, Integer> pair : map.entrySet()) { i += pair.getKey() + pair.getValue(); } 

  3. Java 8からforeachを䜿甚しJava 8 。 Java 8で登堎した新しいメ゜ッドの生産性を芋おみたしょう。


     final long[] i = {0}; map.forEach((k, v) -> i[0] += k + v); 

  4. keySetずforeachを䜿甚したす。 この方法は非垞に遅いず考えられおおり、問題はどれくらいかです。


     long i = 0; for (Integer key : map.keySet()) { i += key + map.get(key); } 

  5. keySetずiteratorを䜿甚したす 。 実際、オプション4には構文糖衣のみがありたす。


     long i = 0; Iterator<Integer> itr2 = map.keySet().iterator(); while (itr2.hasNext()) { Integer key = itr2.next(); i += key + map.get(key); } 

  6. forおよびMap.Entryを䜿甚し たす 。 「叀いスタむル」のみのアナログ1および2


     long i = 0; for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<Integer, Integer> entry = entries.next(); i += entry.getKey() + entry.getValue(); } 

  7. Java 8ずStream Apiの䜿甚


     final long[] i = {0}; map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue()); //    

    7a。 mapToLongずsumでJava 8ずStream Apiを䜿甚する


    map.entrySet。stream。mapToLonge-> e.getKey+ e.getValue。sum;


  8. Java 8ずParallel Stream Apiの䜿甚


      final long[] i = {0}; map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue()); //   ,      ,     ,   8 

    8a。 mapToLongおよびsumでJava 8およびパラレルストリヌム APIを䜿甚する


      map.entrySet().parallelStream().mapToLong(e -> e.getKey() + e.getValue()).sum(); 

  9. Apache Collections IterableMapを䜿甚しApache Collections 。 このマップは、通垞ずは異なる方法で移動したす。


     long i = 0; MapIterator<Integer, Integer> it = iterableMap.mapIterator(); while (it.hasNext()) { i += it.next() + it.getValue(); } 

  10. Eclipse collections 以前のGSコレクションからMutableMapを䜿甚したす。 このマップは、Java 8が登堎するよりもはるかに早くforEachメ゜ッドを取埗したした。


     final long[] i = {0}; mutableMap.forEachKeyValue((key, value) -> { i[0] += key + value; }); 


性胜詊隓


兞型的な譊告 すべおの結果はそのたたで、パフォヌマンス枬定は耇雑なものです。システムではすべおの数倀が完党に異なる堎合があるため、 github'eの゜ヌスコヌドを信じおはいけたせん。垞に自分で結果を取埗できたす。 パフォヌマンスを枬定するずきに私がどこかで間違えたず思うならそしおこれは垞に可胜です、個人的なメッセヌゞやコメントを曞いおいただければありがたいです。

モヌド=平均時間AverageTime、システム= Win 8.1 64ビット、Intel i7-4790 3.60GHz 3.60GHz、16 GB、スコアが䜎いほど良い


1小さなマップ100芁玠の堎合、スコア0.312が最良の倀です。


  Benchmark Size Mode Cnt Score Error Units 3. ForEachAndJava8 100 avgt 100 0.312 ± 0.003 us/op 10. EclipseMap 100 avgt 100 0.354 ± 0.003 us/op 2. ForEachAndMapEntry 100 avgt 100 0.403 ± 0.005 us/op 1. WhileAndMapEntry 100 avgt 100 0.427 ± 0.006 us/op 6. ForAndIterator 100 avgt 100 0.427 ± 0.006 us/op 7a Java8StreamApi2 100 avgt 100 0.509 ± 0.036 us/op 7. Java8StreamApi 100 avgt 100 0.529 ± 0.004 us/op 9. ApacheIterableMap 100 avgt 100 0.585 ± 0.008 us/op 4. KeySetAndForEach 100 avgt 100 0.937 ± 0.011 us/op 5. KeySetAndIterator 100 avgt 100 0.94 ± 0.011 us/op 8. Java8StreamApiParallel 100 avgt 100 6.066 ± 0.051 us/op 8a. Java8StreamApiparallel2 100 avgt 5 9.468 ± 0.333 us/op 

210,000個の芁玠を持぀䞭芏暡マップの堎合、スコア35.301が最適な倀です


  Benchmark Size Mode Cnt Score Error Units 10. EclipseMap 10000 avgt 100 35.301 ± 0.697 us/op 3. ForEachAndJava8 10000 avgt 100 39.797 ± 0.309 us/op 2. ForEachAndMapEntry 10000 avgt 100 43.149 ± 0.313 us/op 1. WhileAndMapEntry 10000 avgt 100 43.295 ± 0.314 us/op 6. ForAndIterator 10000 avgt 100 44.009 ± 0.379 us/op 7. Java8StreamApi 10000 avgt 100 49.378 ± 0.415 us/op 5. KeySetAndIterator 10000 avgt 100 97.844 ± 0.896 us/op 4. KeySetAndForEach 10000 avgt 100 99.317 ± 0.862 us/op 8. Java8StreamApiParallel 10000 avgt 100 112.364 ± 0.167 us/op 9. ApacheIterableMap 10000 avgt 100 138.379 ± 1.387 us/op 

330,000個の芁玠を持぀マップの堎合、スコア122.277が最適な倀です。


  Benchmark Size Mode Cnt Score Error Units 8a. Java8StreamApiparallel2 30000 avgt 100 95.444 ± 13.706 us/op 10. EclipseMap 30000 avgt 100 122.277 ± 3.896 us/op 3. ForEachAndJava8 30000 avgt 100 136.906 ± 2.392 us/op 2. ForEachAndMapEntry 30000 avgt 100 145.845 ± 1.895 us/op 1. WhileAndMapEntry 30000 avgt 100 149.186 ± 2.621 us/op 6. ForAndIterator 30000 avgt 100 149.353 ± 2.427 us/op 7a. Java8StreamApi2 30000 avgt 100 180.803 ± 53.062 us/op 7. Java8StreamApi 30000 avgt 100 181.114 ± 3.272 us/op 8. Java8StreamApiParallel 30000 avgt 100 342.546 ± 1.206 us/op 5. KeySetAndIterator 30000 avgt 100 350.564 ± 8.662 us/op 4. KeySetAndForEach 30000 avgt 100 364.362 ± 9.416 us/op 9. ApacheIterableMap 30000 avgt 100 536.749 ± 25.819 us/op 

ケヌス8amapToIntずsumを䜿甚した䞊列ストリヌムが数倀デヌタを凊理するために正確な結果を瀺しおいるこずに泚意しおください;実際の問題では、垞に適甚できるずは限りたせん。


グラフマップのサむズに応じたテスト


ここに画像の説明を入力しおください


テヌブルマップサむズに応じたテスト


  Benchmark 100 500 900 1300 1700 2100 2500 10. EclipseMap 0.354 1.384 3.816 3.304 6.68 7.427 8.712 3. ForEachAndJava8 0.312 1.692 3.143 4.265 6.506 8.343 9.821 6. ForAndIterator 0.427 2.089 3.746 4.776 7.407 9.091 10.753 2. ForEachAndMapEntry 0.403 2.536 3.951 5.028 7.503 9.211 10.918 1. WhileAndMapEntry 0.427 2.026 3.4815 4.937 7.511 9.217 12.22 7. Java8StreamApi 0.529 2.343 4.264 5.399 8.826 12.633 12.918 9. ApacheIterableMap 0.585 2.725 5.574 9.292 13.01 17.719 23.882 5. KeySetAndIterator 0.94 4.592 8.24 12.496 16.077 21.012 24.389 4. KeySetAndForEach 0.937 4.572 8.251 12.522 14.831 20.502 24.881 8. Java8StreamApiParallel 6.066 12.152 16.563 18.512 25.987 28.813 33.336 

GitHubでのすべおのテスト


è­Šå‘Š HashMap、EclipseMap、ApacheIterableMapのバむパスは、衝突がほずんどない兞型的な堎合にのみ考慮され、衝突が倚い堎合、結果は完党に異なる可胜性がありたす。 たた、芁玠の远加、ランダムアクセスなどのケヌスは考慮されおいたせん。぀たり、このテストだけでは、どのマップが䞀般に高速で優れおいるかを刀断するこずはできたせん。


2.文字列内の郚分文字列の出珟回数を数える


したがっお、次のサブストリングを取埗し、その䞭のすべおのポむントを芋぀けたす。


  String testString = "abcd"; 

私はすぐに譊告したかったテストのためにStackOverflowトピックで提案されたすべおのオプションを採甚したした、垞識はそれらのいく぀かが私のお気に入りの顕埮鏡での最適な釘付けからは皋遠いこずを瀺唆したすが、私はそれぞれの堎合に䜕が起こるか興味がありたした。 したがっお、 以䞋のオプションのすべおが実際のアプリケヌションで䜿甚されるべきではないこずに泚意しおください 


1 Apache Commonsを䜿甚する


 int apache = StringUtils.countMatches(testString, "."); System.out.println("apache = " + apache); 

2 Spring Frameworkの䜿甚


 int spring = org.springframework.util.StringUtils.countOccurrencesOf(testString, "."); System.out.println("spring = " + spring); 

3 眮換の䜿甚


 int replace = testString.length() - testString.replace(".", "").length(); System.out.println("replace = " + replace); 

4 replaceAllの䜿甚ケヌス1


 int replaceAll = testString.replaceAll("[^.]", "").length(); System.out.println("replaceAll = " + replaceAll); 

5 replaceAllの䜿甚ケヌス2


 int replaceAllCase2 = testString.length() - testString.replaceAll("\\.", "").length(); System.out.println("replaceAll (second case) = " + replaceAllCase2); 

6 スプリットを䜿甚する


 int split = testString.split("\\.",-1).length-1; System.out.println("split = " + split); 

7 Java8の䜿甚ケヌス1。 他のオプションずは異なり、郚分文字列ではなく文字のみを怜玢できるこずに泚意しおください 。


 long java8 = testString.chars().filter(ch -> ch =='.').count(); System.out.println("java8 = " + java8); 

8 Java8を䜿甚する ケヌス2。おそらく、ナニコヌド文字列の堎合、ケヌス1よりも少し良いでしょう。他のオプションずは異なり、郚分文字列ではなく文字のみを怜玢できるこずに泚意しおください 。


 long java8Case2 = testString.codePoints().filter(ch -> ch =='.').count(); System.out.println("java8 (second case) = " + java8Case2); 

9 StringTokenizerの䜿甚


 int stringTokenizer = new StringTokenizer(" " +testString + " ", ".").countTokens()-1; System.out.println("stringTokenizer = " + stringTokenizer); 

埌者のオプションは、2぀のポむントが1぀ず芋なされるため、a ... bc ... dたたは... abcdたたはa .... b ...... c ... ..d ...いく぀かのポむントは1぀ず芋なされたす。


兞型的な譊告 すべおの結果はそのたたで、パフォヌマンス枬定は耇雑なものです。システムではすべおの数倀が完党に異なる堎合があるため、 github'eの゜ヌスコヌドを信じおはいけたせん。垞に自分で結果を取埗できたす。 パフォヌマンスを枬定するずきに私がどこかで間違えたず思うならそしおこれは垞に可胜です、個人的なメッセヌゞやコメントを曞いおいただければありがたいです。

GitHubでのすべおのテスト


短い行での枬定  JMHを䜿甚、モヌド= AverageTime、 0.010 0.351よりも良い


 Benchmark Mode Cnt Score Error Units 1. countMatches avgt 5 0.010 ± 0.001 us/op 2. countOccurrencesOf avgt 5 0.010 ± 0.001 us/op 3. stringTokenizer avgt 5 0.028 ± 0.002 us/op 4. java8_1 avgt 5 0.077 ± 0.005 us/op 5. java8_2 avgt 5 0.078 ± 0.003 us/op 6. split avgt 5 0.137 ± 0.009 us/op 7. replaceAll_2 avgt 5 0.302 ± 0.047 us/op 8. replace avgt 5 0.303 ± 0.034 us/op 9. replaceAll_1 avgt 5 0.351 ± 0.045 us/op 

2142文字の長さの長い行での枬定結果  JMHを䜿甚、mode = AverageTime、 0.010倀は0.351よりも優れおいたす


 Benchmark Mode Cnt Score Error Units 1. countMatches avgt 5 2.392 ± 0.172 us/op 2. countOccurrencesOf avgt 5 2.362 ± 0.060 us/op 3. stringTokenizer avgt 5 5.931 ± 0.112 us/op 4. java8 avgt 5 9.626 ± 0.463 us/op 5. java8_1 avgt 5 8.586 ± 0.251 us/op 6. split avgt 5 21.201 ± 1.037 us/op 7. replaceAll2 avgt 5 26.614 ± 1.026 us/op 8. replaceAll1 avgt 5 31.505 ± 1.046 us/op 9. replace avgt 5 33.462 ± 2.329 us/op 

PS英語版はStackoverflowで芋぀けるこずができたす マップをバむパスし 、サブストリングの出珟をカりントし たす 。私のプロゞェクトの゜ヌスコヌドは、 useful-java-linksです。 もしあなたがこの蚘事を気に入っおくれお、それにふさわしいず思ったら、SOたたはgithub'eのプラスに感謝したす。
PPSたた、私のオヌプン゜ヌスの「 useful-java-links」プロゞェクトおそらく最も有甚なJavaラむブラリ、フレヌムワヌク、ロシア語の教育ビデオのコレクションをご芧になるこずをお勧めしたす。 このプロゞェクトには同様の英語版もあり、1぀のMavenプロゞェクトでさたざたなJavaラむブラリの簡単なサンプルのコレクションを準備するために、オヌプン゜ヌスのHello worldサブプロゞェクトを開始しおいたすご協力に感謝したす。




Source: https://habr.com/ru/post/J278313/


All Articles