Выделение памяти Java на стеке по сравнению с "кучей"

Я чувствую себя подобно новичку для того, чтобы задать этот вопрос - но почему случается так, что, когда я передаю Набор ниже в мой метод и указываю на него на новый HashSet, он все еще выходит как EmptySet? Это, потому что локальные переменные выделяются на стеке, и таким образом, мое новое сдувается, когда я выхожу из метода? Как я мог достигнуть функционального эквивалента?

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>();

    }

}
6
задан Amir Afghani 20 July 2010 в 01:28
поделиться

5 ответов

Java передает ссылки по значению, считайте mySet просто копией ссылки foo . В недействительном тесте (Set mySet) переменная mySet является просто локальной переменной внутри этой функции, поэтому установка для нее другого значения не влияет на вызывающего в ] main .

mySet ссылается (или "указывает на", если хотите) на тот же набор, что и переменная foo в main .

Если вы хотите изменить ссылку в main, вы можете сделать, например:

foo = test(); //foo can't be final now though
 public static Set<Integer>  test() {
   return new HashSet<Integer>();
}
8
ответ дан 8 December 2019 в 13:43
поделиться

Не уверен, что понимаю вопрос. В методе test вы инстанцируете новый набор и присваиваете его локальной переменной mySet. После этого mySet больше не будет ссылаться на тот же набор, что и foo в Main.

Когда вы возвращаетесь из метода, foo по-прежнему ссылается на исходный emptySet(), а HashSet, созданный в методе, будет помечен для сбора мусора.

0
ответ дан 8 December 2019 в 13:43
поделиться

... Это потому, что локальные переменные размещены в стеке, и поэтому мой новый сдулся, когда я выйду из метода?

Нет. Это из-за семантики передачи аргументов Java.

Аргументы Java передаются «по значению», но в случае типа объекта или массива передаваемое значение является ссылкой на объект / массив. Когда вы создаете и назначаете новый объект набора для mySet , вы просто устанавливаете локальную переменную / параметр. Поскольку Java использует передачу по значению, это не влияет на переменную foo в методе main .

Когда вы входите в метод test , у вас есть две копии ссылки на экземпляр HashSet , созданные в методе main ; один в foo и один в mySet . Затем ваш код заменяет ссылку в mySet ссылкой на вновь созданный HashSet , но эта новая ссылка не возвращается обратно вызывающей стороне. (Вы можете изменить свой код, чтобы передать его обратно ... например, в результате метода test . Но вы должны сделать это явно.)

Хорошо, однако - если бы я выполнял добавление или какую-либо другую операцию в вызове моего метода, это выделение было бы сохранено.Почему?

Это потому, что когда вы вызываете метод экземпляра, используя ссылку в foo или mySet , этот метод выполняется для объекта ( HashSet ]), на который ссылается ссылка. Предполагая, что две ссылки указывают на один и тот же объект, ваше «распределение будет сохранено». Или, точнее, вы можете наблюдать эффекты операций с одной ссылкой на объект через операции с другими ссылками на тот же объект.

Просто помните, что метод Java вызывает копии ссылок на объект, а не на сами объекты.

Кстати, вы не сможете добавлять элементы в набор, возвращаемый Collections.emptySet () . Этот установленный объект неизменен. Вызов (например) add вызовет исключение.

8
ответ дан 8 December 2019 в 13:43
поделиться
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
    }
}

Как я могу достичь функциональности эквивалент?

Я не уверен, понял ли я этот вопрос, вы ищете возврат?

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

Теперь, если вы назначите foo для того, какой тест возвращает, у вас есть «функциональный эквивалент»?

0
ответ дан 8 December 2019 в 13:43
поделиться

Ваше 'foo' ссылается на пустое множество в вызове test(), вызов теста не изменил этот объект, и поэтому он все еще остается пустым множеством при возврате оттуда.

Внутри метода test() 'mySet' - это просто локальная ссылка, которая ссылается на исходное множество (foo) при входе, а когда вы присвоили этому множеству новый HashSet, вы потеряли ссылку на исходное множество. Но все эти эффекты полностью локальны для метода test(), потому что java просто передала test() дубликат ссылки на исходное множество.

Теперь, внутри test(), поскольку у вас есть ссылка на исходный объект, вы можете изменить этот объект. Например, вы можете добавить элементы в этот набор. Но вы не можете изменить ссылку в вызывающей функции, вы можете изменить только то, на что она ссылается. Поэтому вы не можете заменить одну коллекцию на другую, а если бы вы хотели получить HashSet в первую очередь, вам пришлось бы создавать HashSet в main().

1
ответ дан 8 December 2019 в 13:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: