क्या रेंज लूप के भीतर नक्शे से चयनित कुंजियों को निकालना सुरक्षित है?


135

कोई मानचित्र से चयनित कुंजियों को कैसे निकाल सकता है? क्या delete()सीमा के साथ संयोजन करना सुरक्षित है , जैसा कि नीचे दिए गए कोड में है?

package main

import "fmt"

type Info struct {
    value string
}

func main() {
    table := make(map[string]*Info)

    for i := 0; i < 10; i++ {
        str := fmt.Sprintf("%v", i)
        table[str] = &Info{str}
    }

    for key, value := range table {
        fmt.Printf("deleting %v=>%v\n", key, value.value)
        delete(table, key)
    }
}

https://play.golang.org/p/u1vufvEjSw

जवाबों:


174

यह सुरक्षित है! आप प्रभावी गो में एक समान नमूना भी पा सकते हैं :

for key := range m {
    if key.expired() {
        delete(m, key)
    }
}

और भाषा विनिर्देश :

मानचित्रों पर पुनरावृति क्रम निर्दिष्ट नहीं है और एक पुनरावृत्ति से दूसरे में समान होने की गारंटी नहीं है। यदि मानचित्र प्रविष्टियां जो अभी तक नहीं पहुंची हैं , तो पुनरावृत्ति के दौरान हटा दी जाती हैं , तो संबंधित पुनरावृत्ति मान उत्पन्न नहीं होंगे। यदि मानचित्र प्रविष्टियां पुनरावृत्ति के दौरान बनाई जाती हैं , तो उस प्रविष्टि को पुनरावृत्ति के दौरान उत्पादित किया जा सकता है या छोड़ दिया जा सकता है। प्रत्येक प्रविष्टि के लिए और एक पुनरावृत्ति से अगले में प्रवेश के लिए विकल्प अलग-अलग हो सकता है। यदि मानचित्र शून्य है, पुनरावृत्तियों की संख्या 0 है।


key.expired अपरिभाषित (टाइप स्ट्रिंग का कोई क्षेत्र या तरीका समाप्त नहीं हुआ है)

4
@kristen - ऊपर वर्णित उदाहरण में, कुंजी एक स्ट्रिंग नहीं होनी चाहिए, बल्कि कुछ कस्टम प्रकार है जो func (a T) expired() boolइंटरफ़ेस को लागू करती है। इस उदाहरण के प्रयोजनों के लिए, आप कोशिश कर सकते हैं: m := make(map[int]int) /* populate m here somehow */ for key := range (m) { if key % 2 == 0 { /* this is just some condition, such as calling expired */ delete(m, key); } }
abanana

बहुत ज्यादा उलझन।
g10guang

150

सेबेस्टियन का जवाब सटीक है, लेकिन मैं जानना चाहता था कि यह सुरक्षित क्यों था, इसलिए मैंने मैप सोर्स कोड में कुछ खुदाई की । ऐसा लगता है कि कॉल करने के लिए delete(k, v), यह मूल रूप से केवल एक ध्वज सेट करता है (साथ ही गणना मूल्य बदल रहा है) वास्तव में मूल्य को हटाने के बजाय:

b->tophash[i] = Empty;

(रिक्त मान के लिए एक स्थिर है 0)

वास्तव में जो नक्शा दिखाई दे रहा है, वह नक्शे के आकार के आधार पर बाल्टी की एक निर्धारित संख्या आवंटित कर रहा है, जो आपके द्वारा 2^B( इस स्रोत कोड से ) आवेषण करते समय बढ़ता है :

byte    *buckets;     // array of 2^B Buckets. may be nil if count==0.

इसलिए आपके द्वारा उपयोग किए जाने की तुलना में लगभग हमेशा अधिक बाल्टी आवंटित की जाती है, और जब आप rangeनक्शे पर ओवर करते हैं, तो यह उस tophashमें प्रत्येक बाल्टी के मूल्य को जांचता है कि 2^Bयह देखने के लिए कि क्या यह उस पर छोड़ सकता है।

संक्षेप में, deleteएक rangeसुरक्षित है क्योंकि डेटा तकनीकी रूप से अभी भी वहां मौजूद है, लेकिन जब यह जांचता है तो tophashयह देखता है कि यह सिर्फ इसे छोड़ सकता है और इसे उस rangeऑपरेशन में शामिल नहीं कर सकता जो आप कर रहे हैं। स्रोत कोड में एक भी शामिल है TODO:

 // TODO: consolidate buckets if they are mostly empty
 // can only consolidate if there are no live iterators at this size.

यह बताता है कि delete(k,v)फ़ंक्शन का उपयोग करना वास्तव में मेमोरी को मुक्त नहीं करता है, बस इसे उन बाल्टी की सूची से हटा देता है जिन्हें आप एक्सेस करने की अनुमति देते हैं। यदि आप वास्तविक मेमोरी को खाली करना चाहते हैं, तो आपको पूरे नक्शे को अगम्य बनाने की आवश्यकता होगी, ताकि कचरा संग्रह में कदम होगा। आप ऐसा किसी लाइन का उपयोग करके कर सकते हैं।

map = nil

2
तो ऐसा लगता है कि आप कह रहे हैं कि मानचित्र से किसी भी मनमाने मूल्य को हटाना सुरक्षित है, न कि केवल 'चालू', सही? और जब यह समय हैश का मूल्यांकन करने का समय आता है जिसे मैंने पहले मनमाने ढंग से हटा दिया है, तो यह सुरक्षित रूप से उस पर छोड़ देगा?
इश्कबाज

@ सही, यह सही है, जैसा कि आप इस खेल के मैदान play.golang.org/p/FwbsghzrsO से देख सकते हैं । ध्यान दें कि यदि आप जिस इंडेक्स को हटाते हैं, वह सीमा में पहला है, तब भी यह दिखाएगा कि इसका पहले से ही k, v के साथ लिखा गया है, लेकिन यदि आप इंडेक्स को पहले वाले के अलावा किसी एक पर सेट करते हैं तो यह पता चलता है कि यह केवल दो कुंजी प्रदर्शित करेगा / तीन के बजाय मूल्य जोड़े और घबराएं नहीं।
वेरन

1
क्या "वास्तव में स्मृति मुक्त नहीं है" अभी भी प्रासंगिक है? मैंने उस स्रोत को खोजने की कोशिश की जो टिप्पणी करता है लेकिन उसे खोज नहीं सकता।
टोनी

11
महत्वपूर्ण नोट: याद रखें कि यह केवल वर्तमान कार्यान्वयन है , और यह भविष्य में बदल सकता है, इसलिए आपको "समर्थन" करने के लिए प्रकट होने वाले किसी भी अतिरिक्त गुणों पर भरोसा नहीं करना चाहिए। आपके पास एकमात्र गारंटी है जो विनिर्देश द्वारा प्रदान की गई हैं, जैसा कि सेबेस्टियन के उत्तर में वर्णित है(कहा कि, गो
इंटर्नल्स की

4

मैं सोच रहा था कि क्या मेमोरी लीक हो सकती है। इसलिए मैंने एक परीक्षण कार्यक्रम लिखा:

package main

import (
    log "github.com/Sirupsen/logrus"
    "os/signal"
    "os"
    "math/rand"
    "time"
)

func main() {
    log.Info("=== START ===")
    defer func() { log.Info("=== DONE ===") }()

    go func() {
        m := make(map[string]string)
        for {
            k := GenerateRandStr(1024)
            m[k] = GenerateRandStr(1024*1024)

            for k2, _ := range m {
                delete(m, k2)
                break
            }
        }
    }()

    osSignals := make(chan os.Signal, 1)
    signal.Notify(osSignals, os.Interrupt)
    for {
        select {
        case <-osSignals:
            log.Info("Recieved ^C command. Exit")
            return
        }
    }
}

func GenerateRandStr(n int) string {
    rand.Seed(time.Now().UnixNano())
    const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    b := make([]byte, n)
    for i := range b {
        b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))]
    }
    return string(b)
}

ऐसा लगता है जैसे जीसी मेमोरी को मुक्त करता है। तो ठीक है।


0

संक्षेप में, हाँ। पिछले जवाब देखें।

और यह भी, यहाँ से :

ianlancetaylor ने 18 फरवरी, 2015 को टिप्पणी की,
मुझे लगता है कि यह समझने की कुंजी यह महसूस करना है कि फॉर / रेंज स्टेटमेंट के शरीर को निष्पादित करते समय, कोई वर्तमान पुनरावृत्ति नहीं होती है। मूल्यों का एक समूह है जो देखा गया है, और उन मूल्यों का एक सेट है जिन्हें नहीं देखा गया है। शरीर को निष्पादित करते समय, कुंजी / मूल्य जोड़े में से एक जो देखा गया है - सबसे हालिया जोड़ी - रेंज स्टेटमेंट के चर (ओं) को सौंपा गया था। उस कुंजी / मूल्य जोड़ी के बारे में कुछ खास नहीं है, यह सिर्फ उन लोगों में से एक है जो पहले से ही पुनरावृत्ति के दौरान देखा गया है।

वह जिस सवाल का जवाब दे रहा है range, वह एक ऑपरेशन के दौरान मानचित्र तत्वों को संशोधित करने के बारे में है , यही कारण है कि वह "वर्तमान पुनरावृत्ति" का उल्लेख करता है। लेकिन यह यहां भी प्रासंगिक है: आप एक सीमा के दौरान चाबियाँ हटा सकते हैं, और इसका मतलब है कि आप उन्हें बाद में सीमा पर नहीं देखेंगे (और यदि आपने उन्हें पहले ही देख लिया है, तो यह ठीक है)।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.