Глядя на то, что быстрее, isEmpty или .count == 0

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

К счастью (или к сожалению), ваш код был рассмотрен руководителем вашей группы, старшим разработчиком iOS, во время вашей еженедельной индивидуальной встречи с ним.

Когда он увидел эти строки кода, он начал улыбаться и сказал: «Технически ваш код правильный, и в этом нет никаких сомнений. Однако это не лучшая практика », - низким голосом.

Он тут же замолчал и повернулся к вам, ожидая вашей очереди спросить, почему. Хотя ему не терпелось поделиться некоторыми мыслями (иначе говоря, показухой), ему пришлось притвориться спокойным, в конце концов, он старший разработчик.

Вы начали немного хмуриться, заставляя себя выглядеть озадаченным (вам лучше сделать это, потому что, вероятно, этого ожидал ваш начальник), и вы спросили: «Не могли бы вы пояснить, что вы имели в виду, говоря о передовой практике?»

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

«Что ж, чтобы проверить, пуст ли массив, вы должны напрямую обратиться к свойству isEmpty, которое имеет сложность O (1). Однако, если вы получите доступ к свойству count, это приведет к итерации всего массива, чтобы узнать количество элементов для массива, что составляет O (n) сложность ».

Он все время кивал головой, пока говорил. «С учетом сказанного, краткий ответ на ваш вопрос заключается в том, что лучше всего использовать isEmpty вместо сравнения свойства count с 0.»

Пока он говорил, вы продолжали кивать в том же темпе, что и он (столько поведенческих навыков, которые нужно изучить…). Когда он закончил свою речь, вы спросили: «Почему доступ к isEmpty является операцией O (1)?»

Он объяснил вам, хотя вы на самом деле этого не очень понимали. Ваш разговор продолжился дополнительными вопросами по этой теме. Вся ваша часовая встреча была использована для обсуждения этого.

Когда вы выходили из его офиса, вы все еще выглядели очень озадаченными из-за слишком большого притока новой информации. Тем не менее, вы узнали кое-что важное: используйте isEmpty вместо count == 0, чтобы проверить, пуст ли массив - Лучшая практика!

Однако, когда вы вернулись в свой офис и заняли место, вы начали задаваться вопросом, правда ли, что isEmpty всегда будет лучше count == 0.

Поскольку Xcode был прямо перед вами, вы решили провести несколько простых экспериментов, чтобы сравнить производительность этих двух подходов. В конце концов, данные вам не лгут!

Результаты симуляции

В таблице ниже показаны результаты моделирования. Код, который использовался для получения результатов, можно найти на GitHub.

Произошло то, что была создана серия целочисленных массивов с числом элементов от 0, 1, 10 до 10 000 000.

Для каждой из операций count == 0 и isEmpty он выполнялся 10 000 раз, чтобы обеспечить надежную производительность.

Как ни странно, вы обнаружили, что isEmpty совсем не превзошел count == 0 даже когда в массиве было 10 миллионов целых чисел.

Очевидно, эти результаты не подтверждают то, что ваш руководитель сказал вам относительно сложности O (n) доступа к свойству count в отличие от сложности O (1) доступа к свойству isEmpty.

Вы не были так уверены в результатах и ​​снова провели эксперимент. Тем не менее были получены аналогичные результаты.

Итак, вы сделали еще один шаг и выполнили эти сравнения для массивов из String и Dictionary. Опять же, производительность обоих подходов была сопоставимой.

Эти результаты, по-видимому, подтверждают, что на самом деле не имеет значения, используете ли вы isEmpty или count == 0, чтобы проверить, пуст ли массив, потому что оба были одинаково быстрыми.

Объяснение

Хотя оправдание вашего руководителя звучало примерно правильно, вы должны задаться вопросом, почему это могло произойти, когда вы видите эти результаты.

Здесь я пытаюсь дать свое объяснение, которое может быть неполным. Таким образом, не стесняйтесь оставлять комментарии, если вам есть что сказать.

Из официальной документации очень ясно, что доступ к свойству isEmpty является операцией O (1), и мы не сомневаемся в этом.

Как показано выше, результаты моделирования подтверждают мнение о том, что count == 0 имеет сравнимую производительность с isEmpty, что предполагает, что доступ к свойству count также является операцией O (1). Является ли это возможным?

Ответ положительный. Но почему? Просто потому, что Array соответствует протоколу RandomAccessCollection. Вот обзор этого протокола из официальной документации.

Коллекции с произвольным доступом могут перемещать индексы на любое расстояние и измерять расстояние между индексами за время O (1).

Следовательно, фундаментальное различие между произвольным доступом и двунаправленным сбором данных состоит в том, что операции, зависящие от перемещения индекса или измерения расстояния, обеспечивают значительно более высокую эффективность.

Например, свойство count коллекции с произвольным доступом вычисляется в O (1) вместо того, чтобы требовать итерации всей коллекции.

Как видно из последнего предложения, в нем четко указано, что свойство count коллекции с произвольным доступом является операцией O (1).

Поскольку оба свойства count и isEmpty являются операциями O (1) для Array как коллекции с произвольным доступом, мы должны ожидать, что для проверки того, пуст ли массив, оба подхода count == 0 и isEmpty имеют одинаковую производительность.

Краткое примечание

К текущему обсуждению относится правильный подход к проверке, являются ли Set и String пустыми.

Я могу сказать вам, что свойство count для Set является операцией O (1), как указано в официальной документации. Таким образом, нет заметной разницы, чтобы проверить, является ли Set пустым, используя count == 0 и isEmpty.

Однако String как набор символов не соответствует вышеупомянутому протоколу RandomAccessCollection, что делает его свойство count операцией O (n).

Таким образом, действительно лучше всего использовать isEmpty, чтобы проверить, является ли String пустым, вместо использования count == 0.

Заключение

Многие программисты Swift, особенно старшие, могут подумать, что использование isEmpty - лучший способ определить, пуст ли массив.

Однако представленные здесь данные подтверждают, что count == 0 имеет сопоставимую производительность. Таким образом, нет веских оснований возражать против использования count == 0 при проверке массива Swift на пустоту.