← Назад к вопросам

Как отсортировать массив структур в Go?

1.0 Junior🔥 211 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Основные подходы к сортировке массивов/срезов структур в Go

В Go для сортировки массивов или срезов структур используется пакет sort из стандартной библиотеки. Поскольку структуры могут иметь несколько полей, необходимо определить правила сравнения элементов. Существует три основных подхода.

1. Использование sort.Slice с функцией сравнения (Go 1.8+)

Наиболее гибкий и современный способ — функция sort.Slice, которая принимает срез и функцию less(i, j int) bool.

package main

import (
	"fmt"
	"sort"
)

type Person struct {
	Name string
	Age  int
	City string
}

func main() {
	people := []Person{
		{"Иван", 30, "Москва"},
		{"Анна", 25, "Санкт-Петербург"},
		{"Петр", 35, "Казань"},
		{"Анна", 28, "Владивосток"},
	}

	// Сортировка по возрасту (по возрастанию)
	sort.Slice(people, func(i, j int) bool {
		return people[i].Age < people[j].Age
	})
	fmt.Println("По возрасту:", people)

	// Сортировка по имени, затем по возрасту
	sort.Slice(people, func(i, j int) bool {
		if people[i].Name != people[j].Name {
			return people[i].Name < people[j].Name
		}
		return people[i].Age < people[j].Age
	})
	fmt.Println("По имени и возрасту:", people)
}

Преимущества этого подхода:

  • Минимальный boilerplate-код
  • Возможность инлайнового описания логики сортировки
  • Легко комбинировать несколько полей
  • Нет необходимости в отдельном типе

2. Реализация интерфейса sort.Interface

Более классический подход — создание пользовательского типа, реализующего три метода интерфейса sort.Interface: Len(), Less(i, j int) bool, Swap(i, j int).

type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }

func main() {
	people := []Person{
		{"Иван", 30, "Москва"},
		{"Анна", 25, "Санкт-Петербург"},
	}

	// Сортировка по возрасту
	sort.Sort(ByAge(people))
	fmt.Println(people)
	
	// Обратная сортировка
	sort.Sort(sort.Reverse(ByAge(people)))
	fmt.Println(people)
}

Когда это полезно:

  • Когда нужна сортировка по одному критерию в нескольких местах кода
  • Для использования sort.Reverse() для обратной сортировки
  • В кодовых базах, написанных до Go 1.8

3. Использование sort.SliceStable для стабильной сортировки

sort.SliceStable сохраняет исходный порядок равных элементов, что важно при сортировке по нескольким полям.

// Стабильная сортировка: при равных именах сохраняется предыдущий порядок
sort.SliceStable(people, func(i, j int) bool {
    return people[i].Name < people[j].Name
})

// Теперь дополнительная сортировка по возрасту
sort.SliceStable(people, func(i, j int) bool {
    if people[i].Name == people[j].Name {
        return people[i].Age < people[j].Age
    }
    return false // Не меняем порядок, если имена разные
})

Практические рекомендации

Выбор подхода:

  • Для большинства случаев используйте sort.Slice — это читаемо и удобно
  • Если нужна стабильность сортировки, используйте sort.SliceStable
  • Интерфейс sort.Interface полезен для повторного использования логики сортировки

Производительность:

  • sort.Slice создает замыкание для каждой сортировки, что может быть менее эффективно при очень частых сортировках
  • Реализация sort.Interface может быть немного быстрее в tight loops
  • На практике для большинства приложений разница незначительна

Сортировка по убыванию:

// Для sort.Slice
sort.Slice(people, func(i, j int) bool {
    return people[i].Age > people[j].Age // Обратное сравнение
})

// Для sort.Interface с использованием sort.Reverse
sort.Sort(sort.Reverse(ByAge(people)))

Работа со сложными структурами:

type Product struct {
    Name      string
    Price     float64
    CreatedAt time.Time
}

products := []Product{...}

// Сортировка по цене (убывание), затем по дате (возрастание)
sort.Slice(products, func(i, j int) bool {
    if products[i].Price != products[j].Price {
        return products[i].Price > products[j].Price
    }
    return products[i].CreatedAt.Before(products[j].CreatedAt)
})

В современном Go разработчики чаще всего выбирают sort.Slice за его простоту и выразительность. Этот подход позволяет легко инкапсулировать логику сравнения прямо в месте сортировки, делая код более читаемым и поддерживаемым.