【初心者向け】shallow compare と deep compare とは?

【初心者向け】shallow compare と deep compare とは?

Shallow CompareDeep Compare は、オブジェクトや配列などのデータ構造を比較する際に用いられる手法です。

Shallow Compare(浅い比較)

結論

オブジェクトの一番上の階層だけを比較する。

※内部にネストされたオブジェクトや配列はチェックしません。

  • プリミティブな値(数値、文字列、真偽値など)は、値そのもので比較
  • オブジェクトや配列は、内容ではなく参照(メモリアドレス)で比較

「メモリアドレス」とは、オブジェクトや配列などの参照型データがメモリ上でどこに存在するかを示す「場所(アドレス)」のことです。

具体① : オブジェクト型の比較

オブジェクト型は参照で比較されるため、同じ内容でも異なるオブジェクトであれば falseとなります。

つまり、オブジェクトや配列の中身ではなく、参照しているアドレスを比較します。

const obj1 = { a: 1, b: 2 };
const obj2 = { a: 1, b: 2 };
console.log(obj1 === obj2); // false
// **異なるメモリアドレスに保存されている別のオブジェクトとなる**

具体② : プリミティブ型の比較

プリミティブ型(数値、文字列、真偽値など)は、値そのものが比較されるため、内容が同じなら trueとなります。

const num1 = 5;
const num2 = 5;
console.log(num1 === num2); // true

const str1 = "hello";
const str2 = "hello";
console.log(str1 === str2); // true

const bool1 = true;
const bool2 = true;
console.log(bool1 === bool2); // true

Shallow Compare が効果的に使えるケース

1.Reactのコンポーネントの再レンダリング制御

コンポーネントのメモ化によって、プロパティが変更されない限り再レンダリングを防ぐことができます。

このとき、ReactはShallow Compareを用いて前回と今回のプロパティが同一かを確認します。

Shallow Compareにより、オブジェクトや配列の参照が同じなら再レンダリングをスキップできるため、パフォーマンスを向上させることができます。

import React from 'react';

// 通常のコンポーネント
const MyComponent = React.memo(({ user }) => {
  console.log("Rendered!");
  return <div>{user.name}</div>;
});

// 親コンポーネント
function ParentComponent() {
  const user = { name: "Alice" };

  return <MyComponent user={user} />;
}

2.useEffectの依存配列での最適化

ReactのuseEffectフックにおいても、依存配列に指定した値がShallow Compareで比較されます。

依存配列の中で指定した値が変更されていない限り、useEffectの実行を防ぐことができます。

userの参照が変わらない限り、useEffectの処理は再実行されません。Shallow Compareにより、オブジェクトの参照が同一であれば更新がないとみなされ、パフォーマンスの向上につながります。

import React, { useEffect, useState } from 'react';

function ExampleComponent({ user }) {
  useEffect(() => {
    // APIリクエストなど
  }, [user]); // userがShallow Compareで変更されない限り、再実行されない
}

パフォーマンス

  • 比較する量が少ない
    • オブジェクトや配列の参照だけをチェックするので、プリミティブ型ならそのまま値を比較し、オブジェクト型や配列の場合は、参照が同一かどうかだけを比較するため処理が簡単で軽量
  • 効率的な比較
    • 状態管理やUIの再レンダリング制御においては、Shallow Compareを利用して比較を行うことで、不要なレンダリングを防ぎつつ、パフォーマンスに与える影響を最小限に抑えられる

Deep Compare(深い比較)

結論

オブジェクトや配列の全ての階層を再帰的に比較して、一致しているかどうかを判定する。

※オブジェクトの中にネストされたデータが含まれていても内容が完全に一致しているかを確認できる。

  • プリミティブ値(数値や文字列など)は、そのまま比較
  • オブジェクトまたは配列であれば、すべてのプロパティや要素を再帰的に比較
    • すべての階層で同じ内容である場合に「等しい」と判断

具体① : 比較

JavaScriptには深い比較機能を持つ組み込み関数はありません。

そのため、自分で実装するか、Lodashのようなライブラリを使用します。

const _ = require('lodash');

const object1 = { key: 'value', inner: { number: 1 } };
const object2 = { key: 'value', inner: { number: 1 } };

console.log(_.isEqual(object1, object2)); // true

Deep Compare が効果的に使えるケース

1.APIレスポンスの完全一致確認

ネスト構造のデータが完全に一致するかを確認するためには、Deep Compareが必要です。

ちなみに、Shallow Compareでは参照が異なるため、falseが返されます。

const apiResponse = {
  user: {
    id: 1,
    name: "Alice",
    preferences: {
      theme: "dark",
      notifications: true
    }
  }
};

const localData = {
  user: {
    id: 1,
    name: "Alice",
    preferences: {
      theme: "dark",
      notifications: true
    }
  }
};

console.log(_.isEqual(apiResponse, localData)); // true

2.テストコードでの期待結果の確認

テストコードでは、特定の関数やAPIの出力が期待通りの構造と内容を持っているか確認する必要があります。

この際、期待するオブジェクトと実際の出力結果がネストされたデータ構造を持つ場合、Deep Compareを使って完全一致しているかを確認することが望ましいです。

3.キャッシュデータの更新判定

キャッシュデータが最新であるかどうかを確認したい場合、取得した新しいデータとキャッシュデータが完全に一致するかどうかを確認できます。

パフォーマンス

  • 比較の負荷が高い
    • すべてのプロパティやそのネストされた要素を再帰的に確認するので、データが大きくてネストが深い場合、1つの比較でも多くの計算が必要
  • 処理時間が長くなる
    • 何重にもネストされたオブジェクトや配列をDeep Compareすると、要素が多ければ多いほど比較の負荷が増加し、パフォーマンスが低下する

まとめ

  • Shallow Compare
    • パフォーマンス最適化のために、UIの再レンダリング制御や依存関係の比較で活用
    • 比較が軽量なため、頻繁に使ってもパフォーマンスに悪影響が少ない
  • Deep Compare
    • データの整合性確認やテストに利用
    • 計算負荷が高いため、頻度を抑えつつ、必要な場面でのみ活用

Anycloudではプロダクト開発の支援を行っています

プロダクト開発をお考えの方はぜひAnycloudにご相談ください。

まずは相談する

記事を書いた人

Matsuura

エンジニア

Matsuura

Twitter

Anycloudでエンジニアしてます!主にFlutterやWebフロントをやっていて、最近はバックエンドやインフラに挑戦・苦戦中。