2010-07-20 21 views
6

Czuję się nowicjuszem, który zadaje to pytanie - ale dlaczego po przekazaniu poniższego zestawu do mojej metody i skierowaniu go do nowego HashSet, nadal pojawia się on jako EmptySet ? Czy to dlatego, że zmienne lokalne są przydzielane na stosie, a więc mój nowy jest zszokowany, kiedy wychodzę z metody? W jaki sposób mogę osiągnąć równoważnik funkcjonalny?Przydzielanie pamięci Java na stos vs stertę

import java.util.HashSet; 
import java.util.Set; 

public class TestMethods { 

    public static void main(final String[] args) { 

     final Set<Integer> foo = java.util.Collections.emptySet(); 
     test(foo); 

    } 

    public static void test(Set<Integer> mySet) { 

     mySet = new HashSet<Integer>(); 

    } 

} 

Odpowiedz

8

Java przechodzi przez wartość odniesienia, pomyśl mySet jak tylko kopia odniesienia foo. W void test(Set<Integer> mySet) zmienna mySet jest po prostu lokalną zmienną w tej funkcji, więc ustawienie jej na coś innego nie wpływa na wywołującego w main.

mySet ma odniesienie (lub "wskaż", jeśli chcesz) ten sam zestaw co zmienna foo w przypadku main.

Jeśli chcesz zmienić odniesienia w głównym, można zrobić np

foo = test(); //foo can't be final now though 
public static Set<Integer> test() { 
    return new HashSet<Integer>(); 
} 
8

... Czy to dlatego, zmienne lokalne są alokowane na stosie, a więc mój nowy jest zszokowana, gdy Opuszczam tę metodę?

Nie. Jest to spowodowane argumentem przechodzącym do semantyki języka Java.

Argumenty Java są przekazywane "według wartości", ale w przypadku typu obiektu lub tablicy wartość, którą przekazujesz, jest odniesieniem do obiektu/tablicy. Podczas tworzenia i przypisywania nowego obiektu zestawu do mySet, wystarczy ustawić lokalną zmienną/parametr. Ponieważ Java używa wartości przekazywanych według wartości, nie ma to wpływu na zmienną foo w metodzie main.

Po wprowadzeniu metody test masz dwie kopie odwołania do instancji HashSet utworzonej w metodzie main; jeden w foo i jeden w mySet. Twój kod następnie zastępuje odniesienie w mySet z odniesieniem do nowo utworzonego HashSet, ale ten nowy odnośnik nie zostanie przekazany z powrotem do osoby dzwoniącej. (Można zmienić swój kod, aby przenieść go z powrotem ... na przykład w wyniku metody test Ale trzeba to zrobić w sposób jawny.).

OK - jednak - jeśli miałbym zrobić add lub inna operacja w ramach wywołania metody, ta alokacja zostanie zachowana. Dlaczego?

To dlatego, że podczas wywoływania metody instancji przy użyciu odniesienia w foo lub mySet, że metoda jest wykonywana na obiekcie (HashSet), że wniosek dotyczy. Zakładając, że te dwa odniesienia wskazują na ten sam obiekt, twój "przydział zostanie zachowany". A dokładniej, możesz obserwować efekty operacji na jednym odnośniku do obiektu poprzez operacje na innych odniesieniach do tego samego obiektu.

Pamiętaj tylko, że metoda Java wywołuje odwołania do obiektu, a nie same obiekty.

Przy okazji nie będzie można dodać elementów do zestawu zwróconego przez Collections.emptySet(). Ten obiekt zbioru jest niezmienny.Wywołanie (na przykład) add spowoduje zgłoszenie wyjątku.

+0

dzieła, jednak - gdybym nie dodawać lub jakaś inna operacja w ciągu moje wywołanie metody, ta alokacja zostanie zachowana. Dlaczego? –

+0

Doskonała odpowiedź Stephen, dziękuję. –

0

Nie jestem pewien, czy rozumiem pytanie. W metodzie test tworzysz nowy zestaw i przypisujesz go do lokalnej zmiennej mySet. mySet nie będzie już odwoływać się do tego samego zestawu, co foo z powrotem w Main.

Po powrocie z metody foo nadal odwołuje się do oryginału emptySet(), a HashSet utworzony w metodzie zostanie oznaczony do usuwania śmieci.

0
import java.util.HashSet; 
import java.util.Set; 

public class TestMethods { 

    public static void main(final String[] args) { 

     final Set<Integer> foo = java.util.Collections.emptySet(); 
     test(foo); 

    } 

    public static void test(Set<Integer> mySet) { 
     // here mySet points to the same object as foo in main 
     mySet = new HashSet<Integer>(); 
     // mySet now points to a new object created by your HashSet constructor call, 
     // any subsequent operations on mySet are no longer associated with foo, because 
     // they are no longer referencing the same object 
    } 
} 

Jak mogę osiągnąć funkcjonalną odpowiednik?

Nie jestem pewien, czy rozumiem to pytanie, czy szukasz zwrotu?

public static Set<Integer> test(Set<Integer> mySet) { 
     for(Integer i : mySet){ 
      // do something?? 
     } 
     mySet = new HashSet<Integer>(); 
     return mySet; 
    } 

Teraz, jeśli przypiszesz foo do tego, co test zwróci, masz "równoważny funkcjonalny"?

1

Twój „foo”, o którym mowa pustym zestawem przechodząc do testu() wywołanie, wywołanie testu nie modyfikować że obiekt, a więc jest jeszcze pusty zestaw po powrocie stamtąd.

W metodzie test(), "mySet" jest po prostu lokalną referencją, która odnosi się do oryginalnego zestawu (foo) przy wpisie, i kiedy wykonałeś przypisanie nowego HashSet do tego odniesienia, straciłeś odniesienie do oryginalnego zestawu. Ale te efekty są całkowicie lokalne dla metody test(), ponieważ java po prostu dała test() duplikat odwołania do oryginalnego zestawu.

Teraz, w ramach testu(), ponieważ masz odniesienie do oryginalnego obiektu, możesz zmodyfikować ten obiekt. Na przykład możesz dodać elementy do tego zestawu. Ale nie możesz zmienić odniesienia w funkcji wywołującej, możesz zmienić tylko to, do czego się odnosi. Tak więc nie możesz zastąpić jednej kolekcji innym, a jeśli chciałbyś mieć HashSet w pierwszej kolejności, musisz wprowadzić nowy HashSet w main().

0

powinieneś przeczytać tę książkę:

„Przewodnik dla programisty Java SCJP Certyfikacji: Kompleksowe Primer (3rd Edition)”

+0

Dzięki za wskaźnik. –

Powiązane problemy