Javaの文字列連結は速度を考慮して、「+」ではなく「StringBuffer」を使うべき

Java

今回は、一般的に言われているJavaの文字列連結について紹介します。


僕は都内でSIerをやっているのですが、新人時代のJava研修で教わったことがあります。


 

文字列連結する場合は「+」演算子を使わずに「StringBuffer」を使え!

 


Java研修の時は”へー。そーなんだ。”くらいでしか考えていませんでした。

理由は性能(スピード)だと教えてもらったかと思いますが、ちゃんと理解できず。。。です。

「百聞を一見し如かず」ということで、実際に検証してみようと思います。


環境情報

実際にJavaのプログラムを作って、僕のローカルマシンで動かす形で検証します。

ローカルマシンのスペックは以下になります。

  • OS:Windows7 Professional
  • CPU:Inter Pentium 2.3GHz
  • メモリ:4.00 GB
  • Java:1.7.0_02

あんまりいいパソコン使ってないんですよね。

でもまあ、検証するには十分な環境かと思います。

「+」演算子で文字列をする

まずは、「+」演算子で文字列を連結した場合のプログラムを作ります。

実際に作成したプログラムは以下となります。

“add+数字”の文字列追加を5万回繰り返すプログラムです。

全て、「+」演算子で文字列を連結しています。

import java.text.SimpleDateFormat;
import java.util.Date;

public class PlusConnect {
     public static void main(String[] args){
        System.out.print("start\r\n");

        //-- 日付フォーマット作成 --//
        SimpleDateFormat fmt = 
            new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
        String str = "";

        //-- 文字列の連携を指定回数だけ実行 --//
        for (int lc = 0; lc < 50000; lc++) {
            str = str + "add" + Integer.toString(lc);

            //-- 一定の間隔でログ出力 --//
            if (lc % 10000 == 0) {
                Date now = new Date();
                System.out.print(fmt.format(now) + 
                ":" + Integer.toString(lc) + 
                "回目のループ終了\r\n");
            }
        }

        //-- 文字列長を表示して終了 --//
        System.out.print(
            "文字列長=" + Integer.toString(str.length()));
        System.out.print("end\r\n");
     }
}

上記プログラムの実行結果は以下になります。

hoge>java -Xms256M -Xmx256m PlusConnect
start
2019/04/28 21:43:53.487:0回目のループ終了
2019/04/28 21:43:55.066:10000回目のループ終了
2019/04/28 21:43:59.256:20000回目のループ終了
2019/04/28 21:44:05.239:30000回目のループ終了
2019/04/28 21:44:12.774:40000回目のループ終了
文字列長=388890
end

コメントが解り辛くて申し訳ないです。

「0回目のループ終了」が開始日時で、「40000回目のループ終了」が終了日時です。

約19秒経過していることがわかります。


「StringBuffer」で文字列を連結する

次に、「StringBuffer」で文字列を連結した場合のプログラムを作ります。

実際に作成したプログラムは以下となります。

「StringBuffer」の「append」を使用して文字列を連結するプログラムです。

「+」演算子と同様に、文字列追加を5万回繰り返すプログラムです。

import java.text.SimpleDateFormat;
import java.util.Date;

public class BufferConnect {
    public static void main(String[] args){
        System.out.print("start\r\n");

        //-- 日付フォーマット作成 --//
        SimpleDateFormat fmt = 
            new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
        StringBuffer buf = new  StringBuffer();

        //-- 文字列の連携を指定回数だけ実行 --//
       	for (int lc = 0; lc <= 50000; lc++) {
            buf = buf.append("add");
            buf = buf.append(Integer.toString(lc));

            //-- 一定の間隔でログ出力 --//
            if (lc % 10000 == 0) {
                Date now = new Date();
                System.out.print(fmt.format(now) + ":" + 
                    Integer.toString(lc) + "回目のループ終了\r\n");
            }
        }

        //-- 文字列長を表示して終了 --//
        System.out.print("文字列長=" + 
            Integer.toString(buf.length()) + "\r\n");
        System.out.print("end\r\n");
    }
}

上記プログラムの実行結果は以下になります。

hoge>java -Xms256M -Xmx256m BufferConnect
start
2019/04/28 21:55:55.539:0回目のループ終了
2019/04/28 21:55:55.620:10000回目のループ終了
2019/04/28 21:55:55.651:20000回目のループ終了
2019/04/28 21:55:55.658:30000回目のループ終了
2019/04/28 21:55:55.664:40000回目のループ終了
文字列長=388890
end

こちらは、1秒もかかっておらず約0.5秒ですね。


結果

性能では以下の結果となりました。


  • 「+」演算子で文字列を連結する=19秒
  • 「StringBuffer」で文字列を連結する=0.5秒

38倍の差です。

あきらかに「StringBuffer」を使用した方が性能がよいということがわかります。

コーディングの手間を考えると+演算子の方が簡単なので、ついつい手を抜いてしまうんですよね。

これから気を付けようと僕も思いました。


しかし上記は、ループを5万回繰り返すといった極端な検証の結果となります。

単純な文字列結合では、気にする程度の差は出なかったりします。


まとめ

やっぱり、Java研修で教えてもらった内容は間違っていなかったという結論になりました。


まとめ
  • 性能を考えると、確実にStringBufferを使った方がいい。
  • +演算子とStringBufferは、約40倍の差が出る(場合がある)。
  • “ループ内で文字列結合する場合はStringBufferを使う!”と認識しておけば問題なさそう!

という訳で、皆さん、文字列連結する場合は面倒くさがらずにStringBufferを使うようにしましょう!


それではまた!



WordPressでブログを立ち上げてからまずやること。メタとフッターをまずは修正しとこう。

ロゴ

こんにちは。

さくさくTECHブロガーの「さく」です。


今回は、WordPressでブログを始めてから最初に修正しておいた方がいいと僕が思っている部分について、修正方法を紹介しておきます。

僕は、ブログをみてくれた人に“このブログはWordPressで作ってるなー”と思われるのが何となく嫌なんです。

しかも、”テーマはSparklingだなー”とか思われると赤面してしまいます。

なので、できるだけ、WordPressで作ってます感は消していきたい。

 

今回は、最低限の修正必要箇所として『メタ』『フッター』の修正方法を紹介します。

でもまあ、WordPress使っているかなんて、みる人がみたらすぐに解っちゃうんですけどね。

メタを修正する

まずはメタです。

僕のWordPressデーマは「Sparkling」なのですが、サイドバーに以下のメタ情報が表示されます。

使用しているテーマによっては、上図のようなメタが表示されます。

う~ん。。。WordPressで作ってます感がバリバリ出てます。

“WordPress.org”なんてもってのほか!

という訳で、メタ情報を修正することにします。

メタ情報は全て削除することも可能です。

その場合は、テーマの編集から可能ですが、今回は削除ではなく編集をおこないます。

修正するファイルを特定する

メタを修正するにはPHPファイルを修正する必要があります。

まずは修正するPHPファイルを特定します。

本ブログはレンタルサーバ上にWordPressがインストールされており、SSHが使用できる状態になっているので、WinScpでサーバに接続します。

WordPressのルートフォルダ

WordPressの画面を構成する要素は、「wp-includes」に格納されている「default-widgets.php」で定義されています。

default-widgets.phpの格納フォルダ

「default-widgets.php」をひらいてみます。

※ソースコードの可読性を上げるために改行していますので、実際のPHPファイルとは少しレイアウトが異なります。


/**
 * Widget API: Default core widgets
 *
 * @package WordPress
 * @subpackage Widgets
 * @since 2.8.0
 */

/** WP_Widget_Pages class */
require_once(ABSPATH.WPINC.'/widgets/class-wp-widget-pages.php');

/** WP_Widget_Links class */
require_once(ABSPATH.WPINC.'/widgets/class-wp-widget-links.php');

/** WP_Widget_Search class */
require_once(ABSPATH.WPINC.'/widgets/class-wp-widget-search.php');

/** WP_Widget_Archives class */
require_once(ABSPATH.WPINC.'/widgets/class-wp-widget-archives.php');

/** WP_Widget_Media class */
require_once(ABSPATH.WPINC.'/widgets/class-wp-widget-media.php');

/** WP_Widget_Media_Audio class */
require_once(
  ABSPATH.WPINC.'/widgets/class-wp-widget-media-audio.php');

/** WP_Widget_Media_Image class */
require_once(
  ABSPATH.WPINC.'/widgets/class-wp-widget-media-image.php');

/** WP_Widget_Media_Video class */
require_once(
  ABSPATH.WPINC.'/widgets/class-wp-widget-media-video.php');

/** WP_Widget_Media_Gallery class */
require_once(
  ABSPATH.WPINC.'/widgets/class-wp-widget-media-gallery.php');

/** WP_Widget_Meta class */
require_once(ABSPATH.WPINC.'/widgets/class-wp-widget-meta.php');

/** WP_Widget_Calendar class */
require_once(ABSPATH.WPINC.'/widgets/class-wp-widget-calendar.php');

/** WP_Widget_Text class */
require_once(ABSPATH.WPINC.'/widgets/class-wp-widget-text.php');

/** WP_Widget_Categories class */
require_once(
  ABSPATH.WPINC.'/widgets/class-wp-widget-categories.php');

/** WP_Widget_Recent_Posts class */
require_once(
  ABSPATH.WPINC.'/widgets/class-wp-widget-recent-posts.php');

/** WP_Widget_Recent_Comments class */
require_once(
  ABSPATH.WPINC.'/widgets/class-wp-widget-recent-comments.php');

/** WP_Widget_RSS class */
require_once(ABSPATH.WPINC.'/widgets/class-wp-widget-rss.php');

/** WP_Widget_Tag_Cloud class */
require_once(
  ABSPATH.WPINC.'/widgets/class-wp-widget-tag-cloud.php');

/** WP_Nav_Menu_Widget class */
require_once(ABSPATH.WPINC.'/widgets/class-wp-nav-menu-widget.php');

/** WP_Widget_Custom_HTML class */
require_once(
  ABSPATH.WPINC.'/widgets/class-wp-widget-custom-html.php');

「require_once」が並んでいるだけですね。

このPHPファイルは、各ウィジェットを読み込むだけの役割であることがわかります。

肝心のメタですが、22行目の「class-wp-widget-media.php 」に定義されているので、このファイルが修正対象のファイルとなります。

PHPファイルを修正する

修正するファイルが特定できたので、実際に修正をおこないます。

修正するファイルは、「widgets」フォルダの「class-wp-widget-media.php 」になります。

class-wp-widget-media.phpの格納場所

/**
 * Widget API: WP_Widget_Meta class
 *
 * @package WordPress
 * @subpackage Widgets
 * @since 4.4.0
 */

/**
 * Core class used to implement a Meta widget.
 *
 * Displays log in/out, RSS feed links, etc.
 *
 * @since 2.8.0
 *
 * @see WP_Widget
 */
class WP_Widget_Meta extends WP_Widget {

    /**
     * Sets up a new Meta widget instance.
     *
     * @since 2.8.0
     */
    public function __construct() {
        $widget_ops = array(
            'classname' => 'widget_meta',
            'description' => __('Login, RSS, WordPress.org links.'),
            'customize_selective_refresh' => true,
        );
        parent::__construct( 'meta', __( 'Meta' ), $widget_ops );
    }

    /**
     * Outputs the content for the current Meta widget instance.
     *
     * @since 2.8.0
     *
     * @param array $args Display arguments including 
     *                   'before_title', 'after_title',
     *                   'before_widget', and 'after_widget'.
     */
    public function widget( $args, $instance ) {
        $title = ! empty( $instance['title'] ) ? 
                 $instance['title'] : __( 'Meta' );

        $title = apply_filters(
            'widget_title', $title, $instance, $this->id_base );

        echo $args['before_widget'];

        if ( $title ) {
            echo $args['before_title'].$title.$args['after_title'];
        }
        ?>
<ul>
    <?php wp_register(); ?>
<li>
    <?php wp_loginout(); ?>
</li>
<li>
    <a href="<?php echo esc_url(get_bloginfo('rss2_url')); ?>">
    <?php _e('Entries <abbr title="Really Simple Syndication">RSS</abbr>'); ?>
</a>
</li>
<li>
    <a href="<?php echo esc_url(get_bloginfo( 'comments_rss2_url')); ?>">
    <?php _e('Comments <abbr title="Really Simple Syndication">RSS</abbr>'); ?>
    </a>
</li>
    <?php
    /**
     * Filters the "Powered by WordPress" text in the Meta widget.
     *
     * @since 3.6.0
     * @since 4.9.0 Added the `$instance` parameter.
     *
     */
    echo apply_filters(
        'widget_meta_poweredby',
        sprintf(
            '<li><a href="%s" title="%s">%s</a></li>',
            esc_url( __( 'https://wordpress.org/' ) ),
            esc_attr__(
'Powered by WordPress, state-of-the-art semantic personal publishing platform.'),
_x( 'WordPress.org', 'meta widget link text' )
        ),
        $instance
    );
    wp_meta();
    ?>
</ul>
    <?php

    echo $args['after_widget'];
    }

    /**
     * Handles updating settings.
     *
     * @since 2.8.0
     *
     * @param array $old_instance Old settings for this instance.
     * @return array Updated settings to save.
     */
    public function update( $new_instance, $old_instance ) {
        $instance          = $old_instance;
        $instance['title'] = 
            sanitize_text_field( $new_instance['title'] );
        return $instance;
    }

    /**
     * Outputs the settings form for the Meta widget.
     *
     * @since 2.8.0
     *
     * @param array $instance Current settings.
     */
    public function form( $instance ) {
        $instance = 
            wp_parse_args((array) $instance, array('title' => ''));
        ?>
<label for=
    "<?php echo $this->get_field_id( 'title' ); ?>">
    <?php _e( 'Title:' ); ?>
</label>
<input class="widefat" id="
<?php echo $this->get_field_id( 'title' ); ?>"
name="<?php echo $this->get_field_name( 'title' ); ?>" type="text"
value="<?php echo esc_attr( $instance['title'] ); ?>" />
<?php
    }
}

今回は「ログイン」「WordPress.org」の2つを削除します。

とはいっても後から復活させたいことがあるかもしれないので、コメントアウトすることにします。

まずは「ログイン」ですが、対象箇所は「class-wp-widget-media.php 」の57行目になります。

PHPのコメントアウトと同様に「<!– –>」で囲みます。

<!– <li><?php wp_loginout(); ?></li> –>

次に「WordPress.org」ですが、対象は60行目から82行目になります。

「 class-wp-widget-media.php 」内で動的にURLを作っていますね。

まとめコメントアウトしてしまいます。

これで作業完了です。

結果を確認する

それでは、メタ情報が修正されているかを実際の画面で確認してみます。

「ログイン」「WordPress.org」が消えていれば完了です。

修正後のメタ情報

フッターを修正する

次にフッターです。

デフォルトでは、フッターに「WordPress.org」が表示されています。

見落としがちですが、ここもしっかり修正しておきたいです。

WordPrssフッター

フッターのコピーライト表記は、修正は簡単です。

WordPressのダッシュボードから、設定 → 一般設定で「サイトのタイトル」を変更すれば、勝手に変わってくれます。

サイトのタイトル

サイトのタイトルは、ほとんどの人がブログ名に更新しているかと思います。

なので、最後にフッターを確認しておきましょう、という感じです。

まとめ

僕が考える、”まずは修正しておけ!”っていうポイントを解説しました。

まとめ
  • WordPressデフォルト表示が恥ずかしい人は、最低限、メタとフッターは修正しよう。
  • メタはPHPファイルを修正すれば変えることができる。
  • フッターのコピーライトは、サイト名を変更していれば勝手に変わってくれる。

それではまた!