Эффективность использования фильтра {где:} против removeAll {где:} при изменении значения параметра - PullRequest
2 голосов
/ 09 апреля 2019

Swift 4.2 представил новую функцию removeAll {where:} .Из того, что я прочитал, он должен быть более эффективным, чем использование фильтра {где:}.У меня есть несколько сценариев в моем коде, как это:

private func getListOfNullDates(list: [MyObject]) -> [MyObject] {
        return list.filter{ $0.date == nil }
            .sorted { $0.account?.name < $1.account?.name }
    }

Однако я не могу использовать removeAll {где:} с параметром, потому что это константа.Поэтому мне нужно переопределить его следующим образом:

private func getListOfNullDates(list: [MyObject]) -> [MyObject] {
        var list = list
        list.removeAll { $0.date == nil }
        return list.sorted { $0.account?.name < $1.account?.name }
    }

Является ли использование функции removeAll еще более эффективной в этом сценарии?Или лучше придерживаться функции фильтра?

Ответы [ 2 ]

2 голосов
/ 09 апреля 2019

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

Как правило, просто используйте removeAll, если вы хотите изменить исходный массив, и filter, если вы этого не сделаете.Если вы определили это как потенциальное узкое место в вашей программе, , затем протестируйте его, чтобы увидеть, есть ли разница в производительности.

1 голос
/ 10 апреля 2019

Спасибо за этот вопрос. ??

Я протестировал обе функции, используя этот код на TIO:

let array = Array(0..<10_000_000)

do {
    let start = Date()
    let filtering = array.filter { $0 % 2 == 0 }
    let end = Date()

    print(filtering.count, filtering.last!, end.timeIntervalSince(start))
}

do {
    let start = Date()
    var removing = array
    removing.removeAll { $0 % 2 == 0 }
    let end = Date()

    print(removing.count, removing.last!, end.timeIntervalSince(start))
}

(Чтобы иметь removingи filtering идентичны, закрытие, переданное removeAll, должно было быть { $0 % 2 != 0 }, но я не хотел давать преимущество ни одному фрагменту, используя оператор сравнения быстрее или медленнее.)

Идействительно, removeAll(where:) быстрее, когда вероятность удаления элементов (назовем это Pr) составляет 50%!Вот результаты тестов:

filter    : 94ms
removeAll : 74ms

Это тот же самый случай, когда Pr меньше 50% .

В противном случае фильтрация происходит быстрее для более высокого Pr.

Следует иметь в виду, что в вашем коде list является изменчивым, и это открывает возможность для случайных модификаций.

Лично я бы выбрал производительность вместо старых привычек, и, в некотором смысле, этот вариант использования более читабелен, поскольку намерение яснее.


Бонус: удаление на месте

Что означает удаление на месте - это замена элементов в массиве таким образом, что элементы кбыть удалены размещены после определенного индекса пивота.Элементы, которые нужно сохранить, - это элементы перед элементом поворота:

var inPlace = array
let p = inPlace.partition { $0 % 2 == 0 } 

Имейте в виду, что partition(by:) не сохраняет первоначальный порядок.

Этот подход часы лучше removeAll(where:)

...