Почему связанный список лучше массива

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В проектировании любой системы ключевую роль играет выбор структуры данных. Сейчас мы с вами попытаемся разобраться с двумя структурами данных, а так же подчеркнем плюсы и минусы каждой. Так же сделаем замеры по памяти.

С чего начнем (Постановка задачи)

Нужно обработать список сущностей. Сама по себе сущность (для простоты) представляет собой модель (объект) в которой всего одно свойство "value".

Model( value: int )

Решение через массив + stdClass

Многие разработчики сразу подумают "если мне нужно обработать список моделей мне нужен массив". Массив самая простая структура данных (На мой взгляд).

Для решения данной проблемы нам подойдет следующий код:

$count = 100;
$list = [];

for ($index = 0; $index <= $count; $index++) {
  $model = new stdClass(); // создаем обьект (модель)
  $model->value = $index;  // указываем значение
  
  $list[$index] = $model;  // вносим новую модель в массив
}

В чем же плюсы:

  1. Проще (мало кода) - чем проще тем лучше скажут многие.

Минусы:

  1. Ох уж этот stdClass. Мало того что нет интерфейса взаимодействия, так еще возможны утечки памяти. (В этой статье описаны его минусы).

  2. Потребляет памяти больше чем ожидалось. В PHP массивы не самые экономные так еще и stdClass кушает больше чем ожидалось.

Решение через массив + Model class

Текущее решение похоже на (Решение через массив + stdClass). Единственное и важное отличие это создание интерфейса (класса модели)

$count = 100;
$list = [];

class Model {
  protected int $value;         // значение модели
  public function __construct(int $value)
  {
    $this->value = $value;
  }
}

for ($index = 0; $index <= $count; $index++) {  
  $list[$index] = new Model($index);  // вносим новую модель в массив
}

Какие плюсы:

  1. Код прост.

  2. Есть интерфейс взаимодействия с моделью.

  3. В виду отсутствия stdClass меньше потребляет памяти.

Какие минусы:

  1. Чуть усложняется работа с массивом (например фильтр возможно потребует нового метода в модель).

  2. В PHP массивы не самые экономные по памяти.

Решение через связанный список

Не многие (в основном профи) могут подумать а может стоит использовать связанный список?

$count = 100;

class LinkedList {
  protected int $value;         // значение модели
  protected ?LinkedList $next = null; // следующий элемент

  public function __construct(int $value)
  {
    $this->value = $value;
  }

  // добавление следующего элемента 
  public function add(LinkedList $next): LinkedList
  {
    $this->next = $next;
    return $next;
  }
}

$root = new Node(0); // связаный список

for($index = 1; $index <= $count; $index++) {
	$node = ($node ?? $root)->add(new Node($index));
}
unset($node);

Какие плюсы:

  1. Есть интерфейс взаимодействия

  2. Меньшее потребление памяти

  3. Многие будущие операции будут выполнятся быстрее (смотря как напишет программист)

Какие минусы:

  1. Чуть усложняется работа с данными так как структура другая фильтр будет работать по другому.

Тесты

Наш тест будет происходить на платформе OnlinePHP.io (Можно стразу зайти и проверить результаты)

Вариант решения PHP 8.2

RAM USAGE

AFTER UNSET

связанный список

8'112.00 B

32.00 B

массив + Model class

8'272.00 B

0.00 B

массив + stdClass

44'632.00 B

416.00 B

Вариант решения PHP 8.1

RAM USAGE

AFTER UNSET

связанный список

8'112.00 B

32.00 B

массив + Model class

13'904.00 B

0.0 B

массив + stdClass

50'264.00 B

416.00 B

Вариант решения PHP 8.0 - 7.4

RAM USAGE

AFTER UNSET

связанный список

8'080.00 B

0.00 B

массив + Model class

13904.00 B

0.0 B

массив + stdClass

50'264.00 B

416.00 B

Вывод

Хоть мы тестили на 100 элементах, даже на них видно какой объем памяти занимал тот или иной вариант решения.

Если ваш продукт работает с большим количеством данных, то самый лучший вариант для вас это "Связанный список". Если ваш продукт еще не работает с большим количеством данных, то мой совет не используйте stdClass.

Полный скрипт (для самостоятельных тестов)

Этот код был написан только для ознакомительных целей (Не используйте его в своих проектах).

$count = 100;

class LinkedList {
	protected int $value;
	protected ?LinkedList $next = null;
	public function __construct(int $value) {
		$this->value = $value;
	}
	public function add(LinkedList $next): LinkedList {
		$this->next = $next;
		return $next;
	}
}

class Model {
	protected int $value;
	public function __construct(int $value) {
		$this->value = $value;
	}
}

// -----------------------------------
// -----------------------------------

$memory_get_usage = memory_get_usage();

echo 'list before - ' . sprintf( '%.2f B', memory_get_usage() - $memory_get_usage ) . "\n";

$root = new LinkedList(0);

for($i = 1; $i <= $count; $i++) {
	$next = ($next ?? $root)->add(new LinkedList($i));
}
unset($next);

echo 'list set ---- ' . sprintf( '%.2f B', memory_get_usage() - $memory_get_usage) . "\n";

unset($root);
echo 'list after -- ' . sprintf( '%.2f B', memory_get_usage() - $memory_get_usage) . "\n\n";

// -----------------------------------
// -----------------------------------

$memory_get_usage = memory_get_usage();

echo 'array + model before - ' . sprintf( '%.2f B', memory_get_usage() - $memory_get_usage) . "\n";
$list = [];
for($index = 0; $index <= $count; $index++) {
	$list[$index] = new Model($index);
}
echo 'array + model set ---- ' . sprintf( '%.2f B', memory_get_usage() - $memory_get_usage) . "\n";
$list = [];
unset($list);
echo 'array + model after -- ' . sprintf( '%.2f B', memory_get_usage() - $memory_get_usage) . "\n\n";

// -----------------------------------
// -----------------------------------

$memory_get_usage = memory_get_usage();

echo 'array + stdClass before - ' . sprintf( '%.2f B', memory_get_usage() - $memory_get_usage) . "\n";
$list = [];
for($index = 0; $index <= $count; $index++) {
	$model = new stdClass();
	$model->value = $index;
	$list[$index] = $model;
}
echo 'array + stdClass set ---- ' . sprintf( '%.2f B', memory_get_usage() - $memory_get_usage) . "\n";
$list = [];
unset($list);
echo 'array + stdClass after -- ' . sprintf( '%.2f B', memory_get_usage() - $memory_get_usage) . "\n\n";

Источник: https://habr.com/ru/articles/752310/


Интересные статьи

Интересные статьи

Первая читалка у меня появилась в 2006 году (если не путаю) Это была Sony 500, отличное устройство в металлическом корпусе и с вездесущей тогда для устройств этой фирмы мемори стикс. Читалка не понима...
Михаил Михайлец, руководитель группы аналитиков направления облачных решений Лиги Цифровой Экономики, рассказал, как его команда попробовала быстро подготовить задачи по классическому ТЗ (ГОСТ 19) в J...
На уроках химии мы слышали об инертных газах. Их еще называют благородными, такое красивое название было дано не с проста, ведь все инертные газы, а именно гелий, неон, аргон, криптон, ксенон, а также...
Тенденция последних пары лет — персонализированные витаминные комплексы. На дом можно заказать сеты биодобавок — в зависимости от индивидуальных потребностей организма. Причем по подписке: новую порци...
Аудиоинтерфейсы окружают нас повсюду: в виде голосовых помощников, навигационной системы в автомобиле и общественном транспорте. Их давно используют в военной технике — речевые информаторы переда...