36
36

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PHP:array_diff, array_intersectで配列の比較を超高速に行う(脱foreach作戦)

Last updated at Posted at 2018-08-11

DBやCSVファイルからデータを取得する際、配列として取得することはよくある。
例えば、DB上の大量のデータを配列として取得して、手元にある別のデータとの重複の照合を行うこともあるだろう。
ここでは、配列同士の比較をできる限り高速に行うことを主眼に置く。
実行速度は処理に要する時間を計測することで、遅い or 速いを判断する。
【PHP】処理の速度を計測する方法
【PHP公式】microtime — 現在の Unix タイムスタンプをマイクロ秒まで返す

##配列の準備

はじめに、10万個の要素を持つ配列を2つ準備する。
$dataには0〜99,999の100,000個の数字
$randomDataには0〜500,000のうちランダムに100,000個の数字
が格納されている。

for ($i = 0; $i < 100000; $i++) {
	$data[] = $i;
}

for ($i = 0; $i < 100000; $i++) {
	$randomData[] = rand(0, 500000);
}

#1. 重複する要素を取得する
##1.1 foreachで普通に比較する

$dataのうち$randomDataに含まれるものを数え上げる際にまず思い浮かぶのは、次の方法だろう。それぞれの要素数が少なければ、すぐに終わるが10万件同士の比較になるとそうはいかない。また、実際の業務ではもっと複雑な処理をしなければならないことも多く、あまり使い勝手は良くない。
何よりもコードが見づらい。

foreach ($data as $i) {
	foreach ($randomData as $j) {
		if ($i == $j) {
			$result1[] = $i;
			break;
		}
	}
}
実行結果
121.19333004951 seconds.
Count: 18016 //重複している要素の数

##1.2 array_intersectを用いる

(PHP4.0.1以上)
【PHP公式】array_intersect — 配列の共通項を計算する
array_intersectは、第一引数のうち第二引数以降に含まれるものが返り値になる。このとき、値の型とキーは保持される。また、値の比較はstring(文字列)にキャストした上で行われ、完全一致したものだけが返される。
実際に今回のコードに適用してみると、驚くほど簡潔で清々しいほどである。
そして実行時間は、1.1と比較して約500分の1である。

$result2 = array_intersect($data, $randomData);
実行結果
0.24257612228394 seconds.
Count: 18016 //同じプログラム内で行ったので同じ重複数

#2. 重複しない要素を取得する
##2.1 foreachで普通に比較する

foreachを使って、ある程度実行速度を速くするためには次のコードが思い浮かんだ。コードの見づらさは、ご容赦を。
**「foreachでも、もっといい方法あるよ」**って方は是非教えていただきたい。
このコードでは、$dataのうち$randomDataに含まれないものを取得している。
実行時間は1.1とほぼ同じ結果となった。

foreach ($data as $i) {
	foreach ($randomData as $j) {
		if ($i == $j) {
			break;
		}
	}
	if ($i != $j) {
		$result1[] = $i;
	}
}
実行結果
121.41330385208 seconds
Count: 82026 //重複していない要素の数

##2.2 array_diffを用いる

(PHP4.0.1以上)
【PHP公式】array_diff — 配列の差を計算する
array_diffは第一引数のうち第二引数以降に含まれないものが返り値になる。このとき、値の型とキーは保持され、値の比較もarray_intersect同様に行われる。
それでは、コードと実行結果を見てみよう。
実行時間は、なんと2.1の約4300分の1である。

$result2 = array_diff($data, $randomData);
実行結果
0.02807092666626 seconds
Count: 82026

##まとめ

array_intersectarray_diffは、配列を高速比較できる素晴らしい関数である。
実際の業務では、もっとデータ量の大きいものを比較することは大いにあり得る。そのようなときは、PHPで準備されている配列用の関数を考慮しながら、実装方針を固めるようにしていきたい。
【PHP公式】配列 関数

36
36
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
36

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?