切片比數組好用在哪

来源:https://www.cnblogs.com/chenjiazhan/archive/2023/06/09/17470268.html
-Advertisement-
Play Games

背景 要集成chatGpt參考我上一篇文章即可。但是,如果要實現官網一樣的效果,逐字輸出,難度就提升了不少了。經過在官網的研究發現它應該是採用了SSE技術,這是一種最新的HTTP交互技術。SSE(Server-Sent Events):通俗解釋起來就是一種基於HTTP的,以流的形式由服務端持續向客戶 ...


1. 引言

在Go語言中,數組和切片都是常見的數據結構,它們經常被用於存儲數據,可以相互替換。本文將介紹Go語言中數組和切片的基本概念,同時詳細探討切片的優勢。從而能夠充分的理解切片相對於數組的優點,更好得對切片進行使用。

2. 基本介紹

2.1 數組

數組是一種固定長度、具有相同類型的元素序列。在Go語言中,數組的長度在創建時確定,並且無法動態增長或縮小。數組的聲明方式為var name [size]Type,其中name是數組的標識符,size是數組的長度,Type是數組存儲的元素類型,下麵是數組使用的基本示例:

package main

import "fmt"

func main() {
        // 聲明一個整數數組
        var numbers [2]int
               
        // 初始化數組元素
        numbers[0] = 1
        numbers[1] = 2
        
        // 訪問數組元素
        fmt.Println("數組中的元素:", numbers[0], numbers[1])
}

在上面的例子中,我們定義了一個長度為2的整數數組,分別對其對其賦值和訪問。

2.2 切片

Go語言中的切片實際上是對底層數組的一個引用。切片的長度可以動態改變,而且可以通過切片表達式或內置的appendcopy函數對切片進行操作。切片的聲明方式為var name []Type,其中name是切片的標識符,Type是切片存儲的元素類型,下麵是切片使用的一個基本的例子:

package main

import "fmt"

func main() {
        // 聲明一個整數切片
        var numbers []int
        // 賦值切片
        numbers = []int{1, 2}
        // 訪問切片元素
        fmt.Println("切片中的元素:", numbers[0], numbers[1]) 
}

2.3 總述

看起來數組和切片在定義和使用上有些相似,但它們在長度、記憶體分配、大小調整和傳遞方式等方面存在重要的區別。接下來,我們將探討切片相對於數組的優勢,並解釋為何在許多情況下選擇切片更加合適。

3. 切片優勢

3.1 動態長度

切片在Go語言中具有動態增長和縮小的能力,這是切片相對於數組的重要優勢之一。通過動態調整切片的長度,我們可以根據需要有效地處理和管理數據。

在Go語言中,我們可以使用內置的append函數向切片中添加元素。append函數接受一個切片和一個或多個元素作為參數,並返回一個新的切片,其中包含原切片的所有元素以及添加的新元素。如果切片的容量不足以容納新元素,append函數會自動進行記憶體分配並擴展底層數組的大小,以容納更多的元素。

以下是一個示例,演示瞭如何使用append函數向切片中添加元素:

package main

import "fmt"

func main() {
    slice := []int{1, 2, 3} // 聲明一個切片

    // 使用 append 函數向切片添加元素
    slice = append(slice, 4)
    slice = append(slice, 5, 6)

    fmt.Println(slice) // 輸出: [1 2 3 4 5 6]
}

通過重覆調用append函數,我們可以根據需要動態地增加切片的長度,而不必擔心底層數組的固定長度。

另外,切片也支持使用切片表達式來創建一個新的切片,該切片是原切片的子序列。通過指定起始和結束索引,我們可以選擇性地提取切片中的一部分數據。以下是一個示例,演示瞭如何使用切片表達式來縮小切片的長度:

package main

import "fmt"

func main() {
    slice := []int{1, 2, 3, 4, 5, 6} // 聲明一個切片

    // 使用切片表達式縮小切片的長度
    slice = slice[1:4] // 選擇索引1到索引3的元素(不包含索引4)

    fmt.Println(slice) // 輸出: [2 3 4]
}

通過調整切片表達式中的起始和結束索引,我們可以靈活地縮小切片的長度,以滿足特定需求。

對於數組而言,在創建時需要指定固定的長度,而且無法在運行時改變長度。這意味著數組的長度是靜態的,無法根據需要進行動態調整。比如下麵示例代碼:

package main

import "fmt"

func main() {
        // 聲明一個長度為2的整數數組
        var numbers [2]int
        // 賦值前5個元素
        numbers[0] = 1
        numbers[1] = 2
        // 這裡無法再繼續賦值
        // numners[2] = 3
}

這裡定義一個長度為2的整數數組,如果元素數超過2時,此時將無法繼續寫入,需要重新定義長度更大的一個整數數組,將舊數組的元素全部拷貝過來,之後才能繼續寫入。

而切片則具有動態長度和靈活性,可以根據需要進行動態調整。切片在處理長度不確定的數據時更加方便和高效。因此,在許多情況下,選擇切片而不是數組可以更好地滿足實際需求。

3.2 隨意切割和連接

切片在Go語言中具有出色的靈活性,可以進行切割和連接等操作。這些操作使得我們能夠輕鬆地處理和操作切片的子序列,以滿足不同的需求。

切片可以通過切片表達式進行切割,即選擇切片中的一部分數據。切片表達式使用起始索引和結束索引來指定切片的範圍。例如,slice[1:4]會返回一個新的切片,包含從索引1到索引3的元素(不包含索引4)。通過切割操作,我們可以獲取切片的子序列,便於對數據進行分析、處理和傳遞。

package main

import "fmt"

func main() {
    slice := []int{1, 2, 3, 4, 5, 6} // 聲明一個切片

    // 切割操作
    subSlice := slice[1:4] // 選擇索引1到索引3的元素(不包含索引4)
    fmt.Println(subSlice) // 輸出: [2 3 4]
}

切片還支持使用內置的append函數進行連接操作,將一個切片連接到另一個切片的末尾。append函數會返回一個新的切片,其中包含原始切片和要連接的切片的所有元素。通過連接操作,我們可以將多個切片合併成一個更大的切片,方便進行統一的處理和操作。

package main

import "fmt"

func main() {
    slice := []int{1, 2, 3, 4, 5, 6} // 聲明一個切片
    // 連接操作
    anotherSlice := []int{7, 8, 9}
    mergedSlice := append(slice, anotherSlice...)
    fmt.Println(mergedSlice) // 輸出: [1 2 3 4 5 6 7 8 9]
}

通過切割操作和連接操作,我們可以按需選擇和組合切片中的元素,使得切片在處理數據時更加靈活和方便。這些操作可以根據具體需求進行自由組合,滿足不同場景下的數據處理要求。

3.3 參數傳遞的性能優勢

在函數參數傳遞和返回值方面,切片具有明顯的優勢,並且能夠避免數據的複製和性能開銷。

將切片作為函數的參數傳遞時,實際上是傳遞切片的引用而不是複製整個切片。相比之下,如果傳遞數組作為參數,會進行數組的複製,產生額外的記憶體開銷和時間消耗。

由於切片傳遞的是引用,而不是複製整個數據,所以在函數參數傳遞時可以大大減少記憶體開銷。無論切片的大小如何,傳遞的開銷都是固定的,只是引用指針的複製。這對於大型數據集合的處理尤為重要,可以顯著減少記憶體占用。

下麵通過一個基準測試,證明使用切片傳遞參數,相比使用數組傳遞參數來說,整體性能更好:

const (
   arraySize   = 1000000 // 數組大小
   sliceLength = 1000000 // 切片長度
)

// 使用數組作為函數參數
func processArray(arr [arraySize]int) int {
   // 避免編譯器優化,正確展示效果
   // 使用 reflect.ValueOf 將數組轉換為 reflect.Value
   arrValue := reflect.ValueOf(&arr).Elem()

   sum := 0
   for i := 0; i < arrValue.Len(); i++ {
      // 使用 reflect.Value 索引操作修改數組元素的值
      arrValue.Index(i).SetInt(2)
   }
   return sum
}

// 使用切片作為函數參數
func processSlice(slice []int) int {
   // 避免編譯器優化
   arrValue := reflect.ValueOf(&slice).Elem()
   sum := 0
   for i := 0; i < arrValue.Len(); i++ {
      // 使用 reflect.Value 索引操作修改數組元素的值
      arrValue.Index(i).SetInt(2)
   }
   return sum
}

// 使用數組作為參數的性能測試函數
func BenchmarkArray(b *testing.B) {
   var arr [arraySize]int
   for i := 0; i < arraySize; i++ {
      arr[i] = i
   }

   b.ResetTimer()
   for i := 0; i < b.N; i++ {
      processArray(arr)
   }
}

// 使用切片作為參數的性能測試函數
func BenchmarkSlice(b *testing.B) {
   slice := make([]int, sliceLength)
   for i := 0; i < sliceLength; i++ {
      slice[i] = i
   }
   b.ResetTimer()
   for i := 0; i < b.N; i++ {
      processSlice(slice)
   }
}

這裡我們定義了BenchmarkArrayBenchmarkSlice 兩個基準測試,分別使用數組和切片來作為參數來傳遞,下麵是這兩個基準測試的運行結果:

BenchmarkArray-4             116           9980122 ns/op         8003584 B/op          1 allocs/op
BenchmarkSlice-4             169           6898980 ns/op              24 B/op          1 allocs/op

其中ns/op 表示每次操作的平均執行時間,即函數執行的耗時。B/op 表示每次操作的平均記憶體分配量,即每次操作分配的記憶體大小。allocs/op 表示每次操作的平均記憶體分配次數。

在這裡例子中,可以看到,數組傳遞參數,每一次操作會分配8003584位元組的記憶體,而使用切片來傳遞參數,每次只會傳遞24位元組的記憶體。而且數組作為參數傳遞也比切片作為參數傳遞的平均執行時間傳遞更長。

這個基準測試的結果也證明瞭,在函數參數傳遞和返回值方面,相對於數組,切片具有明顯的優勢,並且能夠避免數據的複製和性能開銷。

4. 總結

本文介紹了Go語言中數組和切片的基本概念,並詳細探討了切片相對於數組的優勢。

數組是一種固定長度、具有相同類型的元素序列,而切片是對底層數組的一個引用,並具有動態長度的能力。切片可以使用切片表達式和內置的append函數進行靈活的切割和連接操作,使得數據的處理更加方便和高效。

切片在函數參數傳遞和返回值方面也具有性能優勢,因為切片傳遞的是引用而不是複製整個數據,可以減少記憶體開銷。

總的來說,切片在處理長度不確定、需要動態調整的數據時更加靈活和高效。在許多情況下,選擇切片而不是數組可以更好地滿足實際需求。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在前端開發中,最常見的字元編碼方案是 UTF-8。UTF-8是一種可變長度的 Unicode 編碼方案,可以表示幾乎所有的字元,並且與 ASCII 相容。由於互聯網的廣泛應用和多語言的支持,UTF-8成為了前端開發中的首選字元編碼方案。 使用UTF-8編碼的好處: 1. 多語言支持 :UTF-8可以 ...
  • 在現有的大環境下,越來越註重用戶體驗,因此對圖片庫的要求也日益攀升。從成本的角度來看,使用 AVIF 格式可以節省大量的網路帶寬和存儲空間,減少網站載入時間,可以改善用戶體驗,進而提高網站的效率和收益,節約大量的費用。 AVIF 的優點在於它可以提供更好的圖像質量和更小的文件大小。與 JPEG 相... ...
  • Map
    基於electron25+vite4+vue3仿製chatgpt客戶端聊天模板ElectronChatGPT。 electron-chatgpt 使用最新桌面端技術Electron25.x結合Vite4.x全家桶技術開發跨端模仿ChatGPT智能聊天程式模板。支持經典+分欄兩種佈局、暗黑+明亮主題模 ...
  • 在應用層下的文件操作只需要調用微軟應用層下的`API`函數及`C庫`標準函數即可,而如果在內核中讀寫文件則應用層的API顯然是無法被使用的,內核層需要使用內核專有API,某些應用層下的API只需要增加Zw開頭即可在內核中使用,例如本章要講解的文件與目錄操作相關函數,多數ARK反內核工具都具有對文件的... ...
  • # ImageIO的應用 # 一、關於IO流 在講imageio之前,我們先來複習一下IO流的使用。 這裡我建立一個Java類,用來實現讀取文檔中的內容,並且能夠識別換行,話不多說,上代碼: ```java package com.Evan.demo; import java.io.Buffered ...
  • 某日二師兄參加XXX科技公司的C++工程師開發崗位第10面: > 面試官:瞭解`sizeof`操作符嗎? > > 二師兄:略微瞭解(不就是求大小的嘛。。) > > 面試官:請講以下如何使用`sizeof`? > > 二師兄:`sizeof`主要是求變數或者類型的大小。直接使用`sizeof(type ...
  • # Rust - 介面設計建議之靈活(flexible) ## 靈活(flexible) ### 代碼的契約(Contract) - 你寫的代碼包含契約 - 契約: - 要求:代碼使用的限制 - 承諾:代碼使用的保證 - 設計介面時(經驗法則): - 避免施加不必要的限制,只做能夠兌現的承諾 - 增 ...
  • # JavaWeb ## 基本概念 web開發: - web:網頁的意思 - 靜態web:提供的數據資源不會發生改變 - 動態web:數據是不斷變化的,現代網站幾乎都是動態網站技術棧: Servlet/JSP:基於java,主推B/S架構,可以 ​ 承載高併發,語法類似ASP ​ ASP:微軟,國內 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...