Go言語で配列より柔軟に使える「スライス」は、開発の中で頻繁に登場します。
中でもよく使うのが「要素の追加」と「削除」ですが、他言語に比べてやや癖のある書き方に戸惑う人も多いです。
この記事では、Goのスライスに対して要素を追加・削除する方法を、初心者でも理解できるように丁寧に解説します。
実際のコード例と一緒に、どんな場面で使うかも紹介するので、すぐに実践で活用できるようになります。
Goのスライスとは?

スライスの基本概念
スライスは可変長の配列のようなものです。
内部的には配列を参照しており、容量や長さを動的に変えられるのが特徴です。
身近な例で考えてみよう:
- 本棚を想像してください
- 普通の本棚(配列):最初に決めたサイズから変更できない
- 伸縮する本棚(スライス):本が増えたら自動で拡張される
配列とスライスの違い
特徴 | 配列 | スライス |
---|---|---|
サイズ | 固定 | 可変 |
宣言方法 | [5]int{1,2,3,4,5} | []int{1,2,3,4,5} |
要素の追加 | できない | append で可能 |
メモリ効率 | 予測しやすい | 動的に調整 |
スライスの基本的な作成方法
package main
import "fmt"
func main() {
// 方法1:リテラルで作成
fruits := []string{"apple", "banana", "cherry"}
fmt.Println(fruits) // [apple banana cherry]
// 方法2:make関数で作成
numbers := make([]int, 3, 5) // 長さ3、容量5
fmt.Println(numbers) // [0 0 0]
// 方法3:空のスライスを作成
var empty []string
fmt.Println(empty) // []
// 方法4:空のスライスを作成(より明示的)
empty2 := []string{}
fmt.Println(empty2) // []
}
スライスの基本情報を取得
package main
import "fmt"
func main() {
fruits := []string{"apple", "banana", "cherry"}
// 長さ(現在の要素数)
fmt.Println("長さ:", len(fruits)) // 長さ: 3
// 容量(メモリ上で確保されている領域)
fmt.Println("容量:", cap(fruits)) // 容量: 3
// 空かどうかの判定
if len(fruits) == 0 {
fmt.Println("空のスライスです")
} else {
fmt.Println("要素があります")
}
}
スライスの内部構造
package main
import "fmt"
func main() {
// スライスは内部的に以下の情報を持っている
slice := []int{1, 2, 3}
fmt.Printf("スライス: %v\n", slice)
fmt.Printf("長さ: %d\n", len(slice)) // 現在の要素数
fmt.Printf("容量: %d\n", cap(slice)) // メモリ上の最大容量
fmt.Printf("アドレス: %p\n", slice) // メモリアドレス
}
この章のまとめ
スライスはGoで配列操作を行ううえで欠かせない基本構造です。次の章では、要素の追加方法を詳しく見ていきます。
スライスへの要素追加
append関数の基本
Goでは標準のappend
関数で要素の追加が可能です。
重要なポイント:
append
は新しいスライスを返す- 元のスライスは変更されない
- 結果を元の変数に代入する必要がある
基本的な要素追加
package main
import "fmt"
func main() {
// 1つの要素を追加
nums := []int{1, 2, 3}
fmt.Println("元のスライス:", nums) // [1 2 3]
nums = append(nums, 4)
fmt.Println("追加後:", nums) // [1 2 3 4]
// さらに追加
nums = append(nums, 5)
fmt.Println("さらに追加:", nums) // [1 2 3 4 5]
}
複数要素の一括追加
package main
import "fmt"
func main() {
nums := []int{1, 2, 3}
// 複数の要素を一度に追加
nums = append(nums, 4, 5, 6, 7)
fmt.Println(nums) // [1 2 3 4 5 6 7]
// 文字列の例
fruits := []string{"apple", "banana"}
fruits = append(fruits, "cherry", "date", "elderberry")
fmt.Println(fruits) // [apple banana cherry date elderberry]
}
スライス同士の結合
package main
import "fmt"
func main() {
// 2つのスライスを結合
a := []int{1, 2, 3}
b := []int{4, 5, 6}
// ...(スプレッド演算子)でスライスを展開
a = append(a, b...)
fmt.Println("結合後:", a) // [1 2 3 4 5 6]
// 文字列スライスの結合例
group1 := []string{"太郎", "花子"}
group2 := []string{"次郎", "美咲"}
allMembers := append(group1, group2...)
fmt.Println("全メンバー:", allMembers) // [太郎 花子 次郎 美咲]
}
先頭への要素追加
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
// 先頭に要素を追加(少し特殊な書き方)
nums = append([]int{1}, nums...)
fmt.Println(nums) // [1 2 3 4]
// 先頭に複数要素を追加
nums = append([]int{-1, 0}, nums...)
fmt.Println(nums) // [-1 0 1 2 3 4]
}
指定位置への要素挿入
package main
import "fmt"
func main() {
fruits := []string{"apple", "cherry", "date"}
// 1番目(appleとcherryの間)にbananaを挿入
index := 1
newElement := "banana"
// 挿入処理:スライスを分割して間に要素を挟む
fruits = append(fruits[:index], append([]string{newElement}, fruits[index:]...)...)
fmt.Println(fruits) // [apple banana cherry date]
}
より読みやすい挿入関数
package main
import "fmt"
// 指定位置に要素を挿入する関数
func insertAt(slice []string, index int, element string) []string {
// 範囲チェック
if index < 0 || index > len(slice) {
return slice // エラーの場合は元のスライスを返す
}
// 新しいスライスを作成して挿入
result := make([]string, len(slice)+1)
copy(result[:index], slice[:index]) // 挿入位置より前をコピー
result[index] = element // 新しい要素を配置
copy(result[index+1:], slice[index:]) // 挿入位置以降をコピー
return result
}
func main() {
fruits := []string{"apple", "cherry", "date"}
fruits = insertAt(fruits, 1, "banana")
fmt.Println(fruits) // [apple banana cherry date]
}
容量とパフォーマンス
package main
import "fmt"
func main() {
// 容量の変化を観察
slice := make([]int, 0, 2) // 初期容量2
fmt.Printf("初期状態 - 長さ: %d, 容量: %d\n", len(slice), cap(slice))
slice = append(slice, 1)
fmt.Printf("1個追加後 - 長さ: %d, 容量: %d\n", len(slice), cap(slice))
slice = append(slice, 2)
fmt.Printf("2個追加後 - 長さ: %d, 容量: %d\n", len(slice), cap(slice))
slice = append(slice, 3) // 容量を超える
fmt.Printf("3個追加後 - 長さ: %d, 容量: %d\n", len(slice), cap(slice))
// 容量が自動的に拡張される(通常は2倍になる)
}
実用例:動的なデータ収集
package main
import "fmt"
func main() {
// ユーザー入力を模擬したデータ収集
var scores []int
// テストの点数を順次追加
testScores := []int{85, 92, 78, 96, 88}
for i, score := range testScores {
scores = append(scores, score)
fmt.Printf("%d回目のテスト後: %v\n", i+1, scores)
}
// 平均を計算
total := 0
for _, score := range scores {
total += score
}
average := float64(total) / float64(len(scores))
fmt.Printf("平均点: %.1f\n", average)
}
この章のまとめ
append
を活用すれば、柔軟にスライスの拡張が可能です。次は、少し難しい要素の削除について解説します。
スライスからの要素削除
削除の基本概念
重要なポイント:
- Goのスライスには直接的な削除関数がない
- 削除はスライスの再構築で実現
- 削除方法は主に3つのパターンがある
方法1:インデックスを指定して削除
基本的な削除パターン
package main
import "fmt"
func main() {
fruits := []string{"apple", "banana", "cherry", "date"}
fmt.Println("元のスライス:", fruits)
// 1番目(banana)を削除
index := 1
fruits = append(fruits[:index], fruits[index+1:]...)
fmt.Println("削除後:", fruits) // [apple cherry date]
}
削除の仕組みを詳しく説明
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30, 40, 50}
fmt.Println("元のスライス:", numbers)
// 2番目(30)を削除する場合
index := 2
// ステップ1:削除位置より前の部分
before := numbers[:index] // [10, 20]
fmt.Println("前の部分:", before)
// ステップ2:削除位置より後の部分
after := numbers[index+1:] // [40, 50]
fmt.Println("後の部分:", after)
// ステップ3:前と後を結合
numbers = append(before, after...)
fmt.Println("削除後:", numbers) // [10 20 40 50]
}
安全な削除関数
package main
import "fmt"
// 指定インデックスの要素を削除する関数
func removeAt(slice []int, index int) []int {
// 範囲チェック
if index < 0 || index >= len(slice) {
return slice // 範囲外の場合は元のスライスを返す
}
// 削除実行
return append(slice[:index], slice[index+1:]...)
}
func main() {
numbers := []int{1, 2, 3, 4, 5}
fmt.Println("元のスライス:", numbers)
// 2番目の要素(3)を削除
numbers = removeAt(numbers, 2)
fmt.Println("削除後:", numbers) // [1 2 4 5]
// 存在しないインデックスを指定(エラーにならない)
numbers = removeAt(numbers, 10)
fmt.Println("範囲外削除:", numbers) // [1 2 4 5] (変更なし)
}
方法2:値を指定して削除
最初に見つかった要素を削除
package main
import "fmt"
// 指定した値を持つ最初の要素を削除
func removeValue(slice []string, value string) []string {
for i, v := range slice {
if v == value {
return append(slice[:i], slice[i+1:]...)
}
}
return slice // 見つからない場合は元のスライスを返す
}
func main() {
fruits := []string{"apple", "banana", "cherry", "banana", "date"}
fmt.Println("元のスライス:", fruits)
// "banana"を削除(最初の1つだけ)
fruits = removeValue(fruits, "banana")
fmt.Println("banana削除後:", fruits) // [apple cherry banana date]
}
指定した値をすべて削除
package main
import "fmt"
// 指定した値を持つすべての要素を削除
func removeAllValues(slice []string, value string) []string {
var result []string
for _, v := range slice {
if v != value {
result = append(result, v)
}
}
return result
}
func main() {
fruits := []string{"apple", "banana", "cherry", "banana", "date"}
fmt.Println("元のスライス:", fruits)
// "banana"をすべて削除
fruits = removeAllValues(fruits, "banana")
fmt.Println("banana全削除後:", fruits) // [apple cherry date]
}
方法3:条件に合致する要素の削除
基本的な条件削除
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println("元のスライス:", numbers)
// 偶数を削除(奇数だけ残す)
var oddNumbers []int
for _, num := range numbers {
if num%2 != 0 { // 奇数の場合
oddNumbers = append(oddNumbers, num)
}
}
fmt.Println("奇数のみ:", oddNumbers) // [1 3 5 7 9]
}
より複雑な条件での削除
package main
import "fmt"
type Student struct {
Name string
Score int
}
func main() {
students := []Student{
{"太郎", 85},
{"花子", 92},
{"次郎", 76},
{"美咲", 88},
{"健太", 65},
}
fmt.Println("全学生:")
for _, s := range students {
fmt.Printf(" %s: %d点\n", s.Name, s.Score)
}
// 80点以上の学生のみを残す
var highScorers []Student
for _, student := range students {
if student.Score >= 80 {
highScorers = append(highScorers, student)
}
}
fmt.Println("\n80点以上の学生:")
for _, s := range highScorers {
fmt.Printf(" %s: %d点\n", s.Name, s.Score)
}
}
効率的な削除方法(in-place削除)
package main
import "fmt"
// メモリ効率の良い削除方法
func removeEvenInPlace(slice []int) []int {
writeIndex := 0
for _, value := range slice {
if value%2 != 0 { // 奇数の場合
slice[writeIndex] = value
writeIndex++
}
}
return slice[:writeIndex] // 有効な部分だけを返す
}
func main() {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println("元のスライス:", numbers)
// 偶数を効率的に削除
numbers = removeEvenInPlace(numbers)
fmt.Println("偶数削除後:", numbers) // [1 3 5 7 9]
}
先頭・末尾要素の削除
package main
import "fmt"
func main() {
fruits := []string{"apple", "banana", "cherry", "date"}
fmt.Println("元のスライス:", fruits)
// 先頭要素を削除
if len(fruits) > 0 {
fruits = fruits[1:]
fmt.Println("先頭削除後:", fruits) // [banana cherry date]
}
// 末尾要素を削除
if len(fruits) > 0 {
fruits = fruits[:len(fruits)-1]
fmt.Println("末尾削除後:", fruits) // [banana cherry]
}
}
削除操作での注意点
package main
import "fmt"
func main() {
// 注意:削除中にスライスを変更するとバグが起きる
numbers := []int{1, 2, 3, 4, 5}
// ❌ 悪い例:削除中にインデックスがズレる
// for i, num := range numbers {
// if num%2 == 0 {
// numbers = append(numbers[:i], numbers[i+1:]...)
// }
// }
// ✅ 良い例:新しいスライスを作成
var result []int
for _, num := range numbers {
if num%2 != 0 { // 奇数のみ残す
result = append(result, num)
}
}
fmt.Println("正しい削除結果:", result) // [1 3 5]
}
この章のまとめ
削除処理ではスライスの再構築が基本になります。次の章では、追加・削除を実務にどう活かすかを紹介します。
実務でのスライス操作活用例

1. データフィルタリング
ユーザー権限の管理
package main
import "fmt"
type User struct {
Name string
Role string
Active bool
}
func main() {
users := []User{
{"admin", "administrator", true},
{"guest", "guest", false},
{"user1", "user", true},
{"user2", "user", false},
{"moderator", "moderator", true},
}
// アクティブなユーザーのみ抽出
var activeUsers []User
for _, user := range users {
if user.Active {
activeUsers = append(activeUsers, user)
}
}
fmt.Println("アクティブユーザー:")
for _, user := range activeUsers {
fmt.Printf(" %s (%s)\n", user.Name, user.Role)
}
// 管理者権限ユーザーのみ抽出
var adminUsers []User
for _, user := range users {
if user.Role == "administrator" || user.Role == "moderator" {
adminUsers = append(adminUsers, user)
}
}
fmt.Println("\n管理者権限ユーザー:")
for _, user := range adminUsers {
fmt.Printf(" %s (%s)\n", user.Name, user.Role)
}
}
Webアプリでのデータフィルタリング
package main
import (
"fmt"
"strings"
)
type Product struct {
ID int
Name string
Price int
Category string
}
// 価格範囲でフィルタリング
func filterByPriceRange(products []Product, minPrice, maxPrice int) []Product {
var filtered []Product
for _, product := range products {
if product.Price >= minPrice && product.Price <= maxPrice {
filtered = append(filtered, product)
}
}
return filtered
}
// カテゴリでフィルタリング
func filterByCategory(products []Product, category string) []Product {
var filtered []Product
for _, product := range products {
if product.Category == category {
filtered = append(filtered, product)
}
}
return filtered
}
// 名前で検索
func searchByName(products []Product, keyword string) []Product {
var filtered []Product
for _, product := range products {
if strings.Contains(strings.ToLower(product.Name), strings.ToLower(keyword)) {
filtered = append(filtered, product)
}
}
return filtered
}
func main() {
products := []Product{
{1, "ノートPC", 80000, "電子機器"},
{2, "スマートフォン", 50000, "電子機器"},
{3, "コーヒーメーカー", 15000, "家電"},
{4, "本棚", 25000, "家具"},
{5, "ゲーミングチェア", 35000, "家具"},
}
// 価格帯で絞り込み(2万円〜6万円)
affordable := filterByPriceRange(products, 20000, 60000)
fmt.Println("2万円〜6万円の商品:")
for _, p := range affordable {
fmt.Printf(" %s: %d円\n", p.Name, p.Price)
}
// カテゴリで絞り込み
electronics := filterByCategory(products, "電子機器")
fmt.Println("\n電子機器:")
for _, p := range electronics {
fmt.Printf(" %s: %d円\n", p.Name, p.Price)
}
// 名前で検索
searchResults := searchByName(products, "ゲーミング")
fmt.Println("\n「ゲーミング」を含む商品:")
for _, p := range searchResults {
fmt.Printf(" %s: %d円\n", p.Name, p.Price)
}
}
2. 動的なデータ構築
設定ファイルの動的生成
package main
import "fmt"
type ConfigItem struct {
Key string
Value string
}
func main() {
var config []ConfigItem
// 基本設定を追加
config = append(config, ConfigItem{"host", "localhost"})
config = append(config, ConfigItem{"port", "8080"})
// 環境に応じて設定を追加
environment := "production" // 実際にはコマンドライン引数や環境変数から取得
if environment == "production" {
config = append(config, ConfigItem{"ssl", "true"})
config = append(config, ConfigItem{"log_level", "error"})
} else {
config = append(config, ConfigItem{"ssl", "false"})
config = append(config, ConfigItem{"log_level", "debug"})
}
// 機能フラグに応じて設定を追加
features := []string{"auth", "cache", "monitoring"}
for _, feature := range features {
config = append(config, ConfigItem{feature + "_enabled", "true"})
}
// 設定を出力
fmt.Println("生成された設定:")
for _, item := range config {
fmt.Printf("%s = %s\n", item.Key, item.Value)
}
}
リアルタイムデータの管理
package main
import (
"fmt"
"time"
)
type LogEntry struct {
Timestamp time.Time
Level string
Message string
}
// ログエントリを追加し、古いエントリを削除
func addLogEntry(logs []LogEntry, entry LogEntry, maxEntries int) []LogEntry {
logs = append(logs, entry)
// 最大エントリ数を超えた場合、古いエントリを削除
if len(logs) > maxEntries {
logs = logs[len(logs)-maxEntries:]
}
return logs
}
// 特定レベル以上のログのみを抽出
func filterLogsByLevel(logs []LogEntry, minLevel string) []LogEntry {
levelPriority := map[string]int{
"debug": 1,
"info": 2,
"warn": 3,
"error": 4,
}
minPriority := levelPriority[minLevel]
var filtered []LogEntry
for _, log := range logs {
if levelPriority[log.Level] >= minPriority {
filtered = append(filtered, log)
}
}
return filtered
}
func main() {
var logs []LogEntry
maxLogs := 5
// ログエントリを順次追加
entries := []LogEntry{
{time.Now(), "info", "アプリケーション開始"},
{time.Now(), "debug", "設定ファイル読み込み"},
{time.Now(), "info", "データベース接続"},
{time.Now(), "warn", "接続リトライ"},
{time.Now(), "error", "データベース接続失敗"},
{time.Now(), "info", "フォールバック処理"},
{time.Now(), "info", "処理完了"},
}
for _, entry := range entries {
logs = addLogEntry(logs, entry, maxLogs)
fmt.Printf("ログ追加: %s [%s] %s\n",
entry.Timestamp.Format("15:04:05"),
entry.Level,
entry.Message)
}
fmt.Printf("\n保持されているログ数: %d\n", len(logs))
// エラー以上のログのみ抽出
criticalLogs := filterLogsByLevel(logs, "error")
fmt.Printf("\n重要なログ(error以上): %d件\n", len(criticalLogs))
for _, log := range criticalLogs {
fmt.Printf(" [%s] %s\n", log.Level, log.Message)
}
}
3. データ集計と分析
売上データの分析
package main
import "fmt"
type Sale struct {
Product string
Amount int
Month int
}
func main() {
sales := []Sale{
{"商品A", 100000, 1},
{"商品B", 150000, 1},
{"商品A", 120000, 2},
{"商品C", 80000, 2},
{"商品A", 110000, 3},
{"商品B", 180000, 3},
}
// 商品別売上集計
productSales := make(map[string]int)
for _, sale := range sales {
productSales[sale.Product] += sale.Amount
}
fmt.Println("商品別売上:")
for product, total := range productSales {
fmt.Printf(" %s: %d円\n", product, total)
}
// 月別売上集計
monthlySales := make(map[int]int)
for _, sale := range sales {
monthlySales[sale.Month] += sale.Amount
}
fmt.Println("\n月別売上:")
for month, total := range monthlySales {
fmt.Printf(" %d月: %d円\n", month, total)
}
// 特定条件の売上を抽出(10万円以上)
var highValueSales []Sale
for _, sale := range sales {
if sale.Amount >= 100000 {
highValueSales = append(highValueSales, sale)
}
}
fmt.Println("\n高額売上(10万円以上):")
for _, sale := range highValueSales {
fmt.Printf(" %s: %d円 (%d月)\n", sale.Product, sale.Amount, sale.Month)
}
}
4. バッチ処理とキュー管理
タスクキューの実装
package main
import (
"fmt"
"time"
)
type Task struct {
ID int
Description string
Priority int
CreatedAt time.Time
}
// タスクを優先度順に挿入
func addTaskByPriority(tasks []Task, newTask Task) []Task {
// 適切な位置を見つけて挿入
for i, task := range tasks {
if newTask.Priority > task.Priority {
// この位置に挿入
return append(tasks[:i], append([]Task{newTask}, tasks[i:]...)...)
}
}
// 最後に追加
return append(tasks, newTask)
}
// 完了したタスクを削除
func removeCompletedTask(tasks []Task, taskID int) []Task {
for i, task := range tasks {
if task.ID == taskID {
return append(tasks[:i], tasks[i+1:]...)
}
}
return tasks
}
// 期限切れタスクを削除
func removeExpiredTasks(tasks []Task, maxAge time.Duration) []Task {
now := time.Now()
var validTasks []Task
for _, task := range tasks {
if now.Sub(task.CreatedAt) <= maxAge {
validTasks = append(validTasks, task)
}
}
return validTasks
}
func main() {
var taskQueue []Task
// タスクを追加(優先度付き)
tasks := []Task{
{1, "データベースバックアップ", 5, time.Now()},
{2, "メール送信", 2, time.Now()},
{3, "レポート生成", 3, time.Now()},
{4, "緊急メンテナンス", 10, time.Now()},
{5, "ログ解析", 1, time.Now()},
}
for _, task := range tasks {
taskQueue = addTaskByPriority(taskQueue, task)
}
fmt.Println("優先度順タスクキュー:")
for i, task := range taskQueue {
fmt.Printf("%d. [優先度:%d] %s\n", i+1, task.Priority, task.Description)
}
// 高優先度タスクを処理(削除)
fmt.Println("\n最高優先度タスクを処理...")
if len(taskQueue) > 0 {
processed := taskQueue[0]
taskQueue = taskQueue[1:]
fmt.Printf("処理完了: %s\n", processed.Description)
}
// 特定タスクを完了(削除)
taskQueue = removeCompletedTask(taskQueue, 2)
fmt.Println("タスクID 2 を完了")
fmt.Println("\n残りのタスク:")
for i, task := range taskQueue {
fmt.Printf("%d. [優先度:%d] %s\n", i+1, task.Priority, task.Description)
}
}
5. ユニークデータの管理
重複削除とデータクリーニング
package main
import "fmt"
// 重複を削除(順序を保持)
func removeDuplicates(slice []string) []string {
seen := make(map[string]bool)
var result []string
for _, item := range slice {
if !seen[item] {
seen[item] = true
result = append(result, item)
}
}
return result
}
// 2つのスライスの共通要素を取得
func intersection(slice1, slice2 []string) []string {
set := make(map[string]bool)
var result []string
// 1つ目のスライスの要素をセットに登録
for _, item := range slice1 {
set[item] = true
}
// 2つ目のスライスで共通要素を探す
seen := make(map[string]bool)
for _, item := range slice2 {
if set[item] && !seen[item] {
result = append(result, item)
seen[item] = true
}
}
return result
}
// 2つのスライスの差分を取得
func difference(slice1, slice2 []string) []string {
set := make(map[string]bool)
var result []string
// 2つ目のスライスの要素をセットに登録
for _, item := range slice2 {
set[item] = true
}
// 1つ目のスライスで2つ目にない要素を探す
for _, item := range slice1 {
if !set[item] {
result = append(result, item)
}
}
return result
}
func main() {
// データクリーニングの例
rawData := []string{
"apple", "banana", "apple", "cherry", "banana", "date", "cherry", "elderberry"
}
fmt.Println("元のデータ:", rawData)
// 重複削除
uniqueData := removeDuplicates(rawData)
fmt.Println("重複削除後:", uniqueData)
// 集合演算の例
group1 := []string{"apple", "banana", "cherry"}
group2 := []string{"banana", "cherry", "date", "elderberry"}
fmt.Println("\nグループ1:", group1)
fmt.Println("グループ2:", group2)
// 共通要素
common := intersection(group1, group2)
fmt.Println("共通要素:", common)
// グループ1のみの要素
onlyGroup1 := difference(group1, group2)
fmt.Println("グループ1のみ:", onlyGroup1)
// グループ2のみの要素
onlyGroup2 := difference(group2, group1)
fmt.Println("グループ2のみ:", onlyGroup2)
}
6. パフォーマンス最適化
効率的なスライス操作
package main
import (
"fmt"
"time"
)
// 非効率な方法:毎回新しいスライスを作成
func inefficientAppend(n int) []int {
var result []int
for i := 0; i < n; i++ {
result = append(result, i)
}
return result
}
// 効率的な方法:事前に容量を確保
func efficientAppend(n int) []int {
result := make([]int, 0, n) // 容量を事前に確保
for i := 0; i < n; i++ {
result = append(result, i)
}
return result
}
// 最も効率的な方法:インデックスで直接設定
func mostEfficientAppend(n int) []int {
result := make([]int, n) // 長さを指定して作成
for i := 0; i < n; i++ {
result[i] = i
}
return result
}
func benchmarkMethod(name string, fn func(int) []int, n int) {
start := time.Now()
result := fn(n)
elapsed := time.Since(start)
fmt.Printf("%s: %v (長さ: %d)\n", name, elapsed, len(result))
}
func main() {
n := 1000000 // 100万要素
fmt.Printf("%d要素の追加パフォーマンス比較:\n", n)
benchmarkMethod("非効率な方法", inefficientAppend, n)
benchmarkMethod("効率的な方法", efficientAppend, n)
benchmarkMethod("最効率な方法", mostEfficientAppend, n)
// メモリ使用量の比較
fmt.Println("\nメモリ効率の例:")
// 容量を指定しない場合
slice1 := []int{}
for i := 0; i < 10; i++ {
slice1 = append(slice1, i)
fmt.Printf("要素数: %d, 容量: %d\n", len(slice1), cap(slice1))
}
fmt.Println("\n容量を事前指定した場合:")
// 容量を事前に指定した場合
slice2 := make([]int, 0, 10)
for i := 0; i < 10; i++ {
slice2 = append(slice2, i)
fmt.Printf("要素数: %d, 容量: %d\n", len(slice2), cap(slice2))
}
}
この章のまとめ
スライスの追加・削除テクニックを覚えれば、柔軟で効率的なデータ操作が可能になります。最後に全体をまとめます。
まとめ

重要ポイント
Goのスライスは柔軟で強力なデータ構造です。以下の要点を押さえておきましょう:
append
関数による追加:新しいスライスを返すため、結果を代入する- 削除はスライスの再構築:直接的な削除関数はないため、スライスを分割・結合
- エラー対策も忘れずに:範囲チェックやnil チェックを行う
- パフォーマンスを意識:事前の容量確保で効率化
スライス操作のベストプラクティス
1. 適切な初期化
目的 | 推奨方法 | 理由 |
---|---|---|
空のスライス作成 | []T{} または make([]T, 0) | nilスライスよりも安全 |
容量が分かっている場合 | make([]T, 0, capacity) | メモリ効率が良い |
固定値で初期化 | []T{value1, value2} | 最も直接的 |
2. エラーハンドリング
// ❌ 悪い例
func badRemove(slice []int, index int) []int {
return append(slice[:index], slice[index+1:]...)
}
// ✅ 良い例
func goodRemove(slice []int, index int) []int {
if index < 0 || index >= len(slice) {
return slice
}
return append(slice[:index], slice[index+1:]...)
}
3. 可読性を重視
// ❌ 悪い例(何をしているかわからない)
result := append(data[:i], data[i+1:]...)
// ✅ 良い例(段階的で理解しやすい)
before := data[:i]
after := data[i+1:]
result := append(before, after...)
覚えておきたいスライス操作パターン
よく使う操作
- 要素の追加:
slice = append(slice, element)
- 要素の削除:
slice = append(slice[:i], slice[i+1:]...)
- 先頭に追加:
slice = append([]T{element}, slice...)
- スライス結合:
slice1 = append(slice1, slice2...)
注意が必要な操作
- 削除中の変更:イテレーション中の削除は避ける
- 容量の管理:大量データでは事前に容量を確保
- nilスライス:操作前にnilチェックを行う
- 型の一致:appendする要素の型が一致していることを確認
コメント