Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Передача параметра по ссылке и по значению


Автор: ChessMaster 13.8.2007, 10:13
Запутался с тем, как передать значение по сслыке, а как по значению. 
Код


public class Zoo{

private List<Monkey> monkeyCage = new ArrayList<Monkey>()
public void addMonkey(Monkey monkey) 

}



public class MAIN{


public satatic void main(){

Monkey monkey = new Monkey();

Zoo zoo = new Zoo();

Zoo.addMonkey(monkey);

Monkey.changeTemperature( int newTemprature);

}


}



Дело в том, что monkey != cageMonkey.get(0);

То есть вклетки сидит мартышка с другой температурой.

Автор: agR 13.8.2007, 11:35
Цитата(ChessMaster @  13.8.2007,  10:13 Найти цитируемый пост)
Запутался с тем, как передать значение по сслыке, а как по значению. 

Честно говоря из примера мало что понятно. 
В Java параметры передаются по значению... для объектных переменных значениями являются ссылки. 
Попытался сделать, че-то по твоей идее, вот что получилось:
Код

package Monkey;

import java.util.ArrayList;

public class MonkeyTest {
    public static void main(String[] args) {
        Zoo zoo = new Zoo();
        Monkey monkey = new Monkey(36.6f);
        Monkey monkey2 = new Monkey(36.6f);
        zoo.addMonkey(monkey);
        monkey.changeTemp(42.2f);
        zoo.addMonkey(monkey2);
        if(monkey == zoo.getList().get(0))  {
            System.out.println("monkey = cageMonkey");
        }
        else {
            System.out.println("monkey != cageMonkey");
        }
        System.out.println("Temp monkey = "+ monkey.getTemp());
        System.out.println("Temp cageMonkey = "+ zoo.getList().get(0).getTemp());
        if(monkey == monkey2) {
            System.out.println("monkey = monkey2");
        }
        else System.out.println("monkey != monkey2");
    }
}
class Monkey {
    public Monkey(float temp) {
        this.temp = temp;
    }
    public float getTemp() {
        return temp;
    }
    public float changeTemp(float newTemp) {
        this.temp=newTemp;
        return temp;
    }
    private float temp;
}
class Zoo {
    public Zoo() {
        monkeyCage = new ArrayList<Monkey>();
    }
    public void addMonkey(Monkey monkey) {
        monkeyCage.add(monkey);
    }
    public ArrayList<Monkey> getList() {
        return monkeyCage;
    }
    private ArrayList<Monkey> monkeyCage;
    
}

П.С. А ты код прям на форуме набирал? smile

Автор: LSD 13.8.2007, 12:34
Объекты всегда передаются по ссылке, а примитивные типы по значению.

Автор: agR 13.8.2007, 12:58
LSD, 
Цитата(LSD @  13.8.2007,  12:34 Найти цитируемый пост)
Объекты всегда передаются по ссылке, а примитивные типы по значению. 

и
Цитата(agR @  13.8.2007,  11:35 Найти цитируемый пост)
В Java параметры передаются по значению... для объектных переменных значениями являются ссылки. 

Как по мне, то те же грабли, тока в профиль.
Читал книгу, так там акцент как раз и делался, что в java передается по значению... и утверждение  "по ссылке" - ошибочно. Кому верить? smile


Автор: LSD 13.8.2007, 13:28
Для объектов передается копия ссылки на этот объект. И мы можем в теле метода изменить переданную копию ссылки, присвоив ей другой объект.

Автор: ChessMaster 13.8.2007, 14:48
Цитата(LSD @  13.8.2007,  13:28 Найти цитируемый пост)
Для объектов передается копия ссылки на этот объект. И мы можем в теле метода изменить переданную копию ссылки, присвоив ей другой объект. 


LSD, а можно пример того как присаивать ссылку другому объекту, чтобы знать как передать по значению. Спасибо smile 

Автор: fixxer 13.8.2007, 15:45
Боюсь, LSD ввел в небольшое заблуждение. Изменение (переприсваивание) ссылки возымеет действие только в пределах метода. Ключевая фраза "передается копия ссылки на этот объект". 

Автор: LSD 13.8.2007, 16:05
Код

  public static void main(String[] args)
  {
    StringBuffer string = new StringBuffer("Abc");
    System.out.println("inital = " + string);
    toLowerCase(string);
    System.out.println("after toLowerCase() = " + string);
    toUpperCase(string);
    System.out.println("after toUpperCase() = " + string);
    toCamelCase(string);
    System.out.println("after toCamelCase() = " + string);
  }

  public static void toLowerCase(StringBuffer str)
  {
    for(int i = 0; i < str.length(); i++)
      str.setCharAt(i, Character.toLowerCase(str.charAt(i)));
  }

  public static void toUpperCase(StringBuffer str)
  {
    StringBuffer newStringBuffer = str;
    for(int i = 0; i < newStringBuffer.length(); i++)
      newStringBuffer.setCharAt(i, Character.toUpperCase(newStringBuffer.charAt(i)));
  }

  public static void toCamelCase(StringBuffer str)
  {
    str = new StringBuffer("blablabla");
    for(int i = 0; i < str.length(); i++)
    {
      if(i % 3 == 0)
        str.setCharAt(i, Character.toUpperCase(str.charAt(i)));
      else
        str.setCharAt(i, Character.toLowerCase(str.charAt(i)));
    }
    System.out.println("inside toCamelCase() = " + str);
  }

Автор: fixxer 13.8.2007, 19:00
Да, безусловно, только пример не отражает фразу
Цитата

И мы можем в теле метода изменить переданную копию ссылки, присвоив ей другой объект. 

Автор: nornad 14.8.2007, 05:27
Цитата(fixxer @  13.8.2007,  22:00 Найти цитируемый пост)
только пример не отражает фразу

А как же это:

Цитата(LSD @  13.8.2007,  19:05 Найти цитируемый пост)
  public static void toCamelCase(StringBuffer str)
  {
    str = new StringBuffer("blablabla");

 smile 

Автор: fixxer 14.8.2007, 10:15
Да, сорри. Не заметил. Ну тогда будут выведены разные значения внутри и вне метода. Наверно LSD это и хотел продемонстрировать.  smile 

Автор: chief39 14.8.2007, 13:05
Цитата(agR @  13.8.2007,  12:58 Найти цитируемый пост)
Как по мне, то те же грабли, тока в профиль.

Нифига се профиль!!! smile))

Цитата(ChessMaster @  13.8.2007,  14:48 Найти цитируемый пост)
LSD, а можно пример того как присаивать ссылку другому объекту, чтобы знать как передать по значению. Спасибо 

Если надо сугубо по значению - тогда clone() объекта и передача клона в качестве параметра.

Автор: LSD 15.8.2007, 15:02
Цитата(chief39 @ 14.8.2007,  14:05)
Цитата(ChessMaster @  13.8.2007,  14:48 Найти цитируемый пост)
LSD, а можно пример того как присаивать ссылку другому объекту, чтобы знать как передать по значению. Спасибо 

Если надо сугубо по значению - тогда clone() объекта и передача клона в качестве параметра.

Ага smile 
Передать по значению нельзя, но можно это дело эмулировать клоном для изменяемых объектов, а для неизменяемых просто передавать как есть.

Автор: Fedrus 24.1.2008, 11:15
Как раз сегодня получил ошибку забыв про "передачу объектов по ссылке".
Я передал как new Object(myObject). В поисках более правильного решения погуглил и 
нашел интересную статью http://www.javable.com/columns/robinson/letters/01/
в ней как раз доказывается что в java 
Цитата(agR @  13.8.2007,  11:35 Найти цитируемый пост)
В Java параметры передаются по значению... для объектных переменных значениями являются ссылки. 

Потом залез на форум и нашел эту тему и окончательно запутался(LSD привык верить) и сегодня сильно не выспался чтоб полностью понять статью может посмотрите и всетаки дадите определенный ответ.

Автор: LSD 24.1.2008, 12:56
По сути тут утверждается одно и то же. Для примитивных типов в стеке создаётся копия этой переменной. Для объекта - создаётся копия ссылки на этот объект.

Как это назвать, передачей копии ссылки, или передачей по значению, при условии что для объектных переменных значением является ссылка, вопрос вторичный. 

Просто классическая передача объекта по значению, подразумевает что создаётся копия этого объекта и именно она и передаётся. Поэтому я предпочитаю говорить, что передаётся копия ссылки.

Автор: Мурлыкатам_ 10.11.2008, 18:20
Простите, что я продолжаю тему, я честно гуглил и честно смотрел статьи на форуме, но так и не понял как передать по ссылке к примеру массив целых чисел.

Задача: отсортировать массив случайных чисел от 0 до 5000, записанных в файл размером 1 мб, т.е. в файле 1 мб этих чисел. 

Решение (сразу сделал под .net): решаю задачу через алгоритм быстрой сортировки (QuickSort: http://ru.wikipedia.org/wiki/Quicksort#Java.2FC.23), получается быстро, но не достаточно. Передаю массив через ref, и о чудо, все сортируется меньше чем за секунду...

Проблема: задача была поставлена для java, реализую все на java и сталкиваюсь с невозможностью передать массив по ссылке, в результате сортировка 100кб массива - 53 секунды. Очевидно проблема в постоянной передачи массива по значению.

Верно ли я понял, что для решения этой задачи мне нужно просто массив завернуть в класс, сделать экземпляр этого класса и вместо массива передавать экземпляр класса для увеличения производительности?

Если нет, то подскажите плз что-нить, потому что уже -  smile 

Автор: powerOn 10.11.2008, 19:44
вообще-то, массивы в java всегда передаются по ссылке...  smile 

Автор: Мурлыкатам_ 10.11.2008, 19:54
Хммм... завтра выкину код. smile Может в руках дело smile

Автор: Дрон 10.11.2008, 21:10
Цитата(powerOn @  10.11.2008,  20:44 Найти цитируемый пост)
вообще-то, массивы в java всегда передаются по ссылке...

Ровно как и в .NET

Поэтому сказанное ниже очень странно:
Цитата(Мурлыкатам_ @  10.11.2008,  19:20 Найти цитируемый пост)
Передаю массив через ref, и о чудо, все сортируется меньше чем за секунду...


Так что ждём код smile


Автор: SoulKeeper 11.11.2008, 10:50
По алгоритму с Википедии вываливается StackOverflowError на 100кб массиве.

P.S. 100кб массив (1024 * 1024 / 4) элементов?

Автор: LSD 11.11.2008, 13:14
Цитата(SoulKeeper @  11.11.2008,  10:50 Найти цитируемый пост)
По алгоритму с Википедии вываливается StackOverflowError на 100кб массиве.

Как тебе это удалось?
Код

import java.util.Random;

public class QSort {
  public static final int ARRAY_SIZE = 8 * 1024 * 1024;

  public static int partition(int[] m, int a, int b) {
    int i = a;
    for(int j = a; j <= b; j++) {
      if(m[j] <= m[b]) {
        int t = m[i];
        m[i] = m[j];
        m[j] = t;
        i++;
      }
    }
    return i - 1;
  }

  public static void quickSort(int[] m, int a, int b) {
    if(a >= b) return;
    int c = partition(m, a, b);
    quickSort(m, a, c - 1);
    quickSort(m, c + 1, b);
  }

  public static void main(String[] args) {
    int[] ints = new int[ARRAY_SIZE];
    Random rnd = new Random();
    for(int i = 0; i < ints.length; i++) {
      ints[i] = rnd.nextInt();
    }

    quickSort(ints, 0, ARRAY_SIZE - 1);

    for(int i = 1; i < ints.length; i++) {
      if(ints[i - 1] > ints[i]) {
        System.err.printf("Array not sorted a[%1$d] = %2$d, a[%3$d] = %4$d; %n", i - 1, ints[i - 1], i, ints[i]);
        break;
      }
    }
  }
}

Автор: Мурлыкатам_ 11.11.2008, 13:39
Цитата(Дрон @  10.11.2008,  21:10 Найти цитируемый пост)
вообще-то, массивы в java всегда передаются по ссылке...

Ровно как и в .NET


Точно  smile 

Все таки хромала реализация  smile 
Я еще чуток поковыряюсь сам, а если не получиться, то напишу, стыдно немного кривоватый код показывать smile

Хотя словом, "немного" я наверное себе зальстил smile

Реализация уважаемого LSD, работает просто выше всяких похвал.

Спасибо!

Автор: SoulKeeper 11.11.2008, 13:49
Цитата(LSD @ 11.11.2008,  13:14)
Как тебе это удалось?

Хмм... таки не вываливается.
Может что-то умудрился поломать пока копипейстил smile

EDIT:
Код

public static final int ARRAY_SIZE = 8 * 1024 * 1024;


тут если 8 поменять на 32, то вывалиться ;)

Автор: LSD 11.11.2008, 14:37
Цитата(SoulKeeper @  11.11.2008,  13:49 Найти цитируемый пост)
тут если 8 поменять на 32, то вывалиться ;)

Дык вываливается с OutOfMemoryError, а не StackOverflowError. Что вполне законно, т.к. массив занимает 128 мегабайт памяти, а по умолчанию JVM берет себе 32 мегабайта. Надо просто запустить с ключём -Xmx140m и все заработает.
OutOfMemoryError это простая нехватка памяти, решается ключами запуска. А вот StackOverflowError уже более серьезная проблема, как правило это бесконечная или слишком "глубокая" рекурсия.

Автор: SoulKeeper 11.11.2008, 16:33
Код

Exception in thread "main" java.lang.StackOverflowError
    at SortDemo.quicksort(SortDemo.java:41)
    at SortDemo.quicksort(SortDemo.java:42)
    at SortDemo.quicksort(SortDemo.java:42)
...


Код

import java.util.Random;

public class SortDemo {

  public static void main(String[] args) {

    int length = 1024 * 1024 * 32;
    int[] arr = new int[length];

    Random rnd = new Random();
    for (int i = 0; i < arr.length; i++) {
      arr[i] = rnd.nextInt(5000);
    }

    long time = System.currentTimeMillis();
    quicksort(arr, 0, length - 1);
    // Arrays.sort(arr);
    System.out.println(System.currentTimeMillis() - time);
    System.out.println("Sorted");
  }

  private static int partition(int[] m, int a, int b) {
    int i = a;
    for (int j = a; j <= b; j++) // просматриваем с a по b
    {
      if (m[j] <= m[b]) // если элемент m[j] не превосходит m[b],
      {
        int t = m[i]; // меняем местами m[j] и m[a], m[a+1], m[a+2] и так далее...
        m[i] = m[j]; // то есть переносим элементы меньшие m[b] в начало,
        m[j] = t; // а затем и сам m[b] «сверху»
        i++; // таким образом последний обмен: m[b] и m[i], после чего i++
      }
    }
    return i - 1; // в индексе i хранится <новая позиция элемента m[b]> + 1
  }

  public static void quicksort(int[] m, int a, int b) // a - начало подмножества, b - конец
  { // для первого вызова: a = 0, b = <элементов в массиве> - 1
    if (a >= b)
      return;
    int c = partition(m, a, b);
    quicksort(m, a, c - 1);
    quicksort(m, c + 1, b);
  }
}




Отличить нехватку памяти от переполнения стэка я еше могу.

Автор: LSD 11.11.2008, 17:25
Цитата
Худший случай. Худшим случаем, очевидно, будет такой, при котором на каждом этапе массив будет разделяться на вырожденный подмассив из одного опорного элемента и на подмассив из всех остальных элементов. Такое может произойти, если в качестве опорного на каждом этапе будет выбран элемент либо наименьший, либо наибольший из всех обрабатываемых.
Худший случай даёт O(n²) обменов, но количество обменов и, соответственно, время работы — это не самый большой его недостаток. Хуже то, что в таком случае глубина рекурсии при выполнении алгоритма достигнет n, что будет означать n-кратное сохранение адреса возврата и локальных переменных процедуры разделения массивов. Для больших значений n худший случай может привести к исчерпанию памяти во время работы алгоритма. Впрочем, на большинстве реальных данных можно найти решения, которые минимизируют вероятность того, что понадобится квадратичное время.
....
Представленные здесь реализации, кроме варианта на языке Паскаль, используют в качестве опорного элемента один из крайних элементов подмассива. Все эти реализации страдают одним общим недостатком: при передаче им уже отсортированного массива в качестве параметра они приводят к самому худшему случаю.

Вот в чем проблема. Надо доработать метод partition().

Автор: SoulKeeper 11.11.2008, 17:29
Честно говоря я вообще не понимаю зачем использовать данный велосипед. Arrays.sort() работает и быстрее и вылеты оверфловы за ним не замечались ;)

Автор: LSD 11.11.2008, 17:39
Ну я считаю, что знать базовые алгоритмы полезно и нужно. Тем более что Arrays.sort() сам использует quicksort, хотя там конечно реализация более грамотная с защитой от подобных проблем.

Автор: SoulKeeper 11.11.2008, 17:47
Что-то не верится что на этом форуме наберется десяток программистов которые смогут написать по памяти хоть пару из 
http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8


Одно дело знать что они существуют, другое - их писать.

Автор: LSD 11.11.2008, 17:59
Пузырек и сортировку слиянием думаю напишут многие smile Не надо их знать на память, достаточно знать что они есть и знать про их особенности (потребление дополнительной памяти, отношение к частично упорядоченным данным и т.п.). Чтобы в случае необходимости знать какой алгоритм стоит использовать (готовую реализацию найти не проблема).

А вообще это все оффтопик.

Автор: Мурлыкатам_ 11.11.2008, 18:06
Я думаю каждый программист использует сортировку в своей жизни  smile 

Поэтому зная алгоритм и его реализацию, лучше один раз написать сортировку самому и понять, как она работает, чем использовать кота в мешке =))) или каждый раз искать алгоритм в инете.

Цитата(SoulKeeper @  11.11.2008,  17:47 Найти цитируемый пост)
Что-то не верится что на этом форуме наберется десяток программистов которые смогут написать по памяти хоть пару из 


Мне тоже не вериться, потому что их число будет значительно больше  smile 

Да и в любом случае понимание алгоритма лучше, чем бездумное использование готового!

Автор: kashka 17.4.2009, 13:02
Теперь мне по крайней мере понятно почему у меня не работает след. (транспонирование двухмерногоа массива)
Код

    public static void trasponseArray(Object[][] array) {        
        
        int n = array.length;
        int m = array[0].length;
        
        Object[][] temp = new Object[n][m];
        
        
        for (int i = 0; i < n; i++) {            
            for (int j = 0; j < m; j++) {
                temp[i][j] = array[i][j];
            }
            System.out.println();
        }        
        
        array = new Object[m][n];
        
        for (int i = 0; i < temp.length; i++) {
            for (int j = 0; j < temp[i].length; j++) {
                array[j][i] = temp[i][j];
            }
        }

        
    }


Так как передаётся не сама ссылка а её копия, хм...Так это что же получается нет никакой другой возможности кроме как return добавлять? 

Автор: kashka 17.4.2009, 14:30
Что то я торможу, второй массив мне и не нужен вовсе:
Код

    public static void trasponseArray(Object[][] array) {        
        
        int n = array.length;
        int m = array[0].length;
        
        for (int i = 0; i < n; i++) {            
            for (int j = i + 1; j < m; j++) {
                Object temp1 = array[i][j];
                Object temp2 = array[j][i];
                array[j][i] = temp1;
                array[i][j] = temp2;
            }            
        }
        
    }


Но работает это конечно только для nxn массивов.

Автор: goodday1941 17.4.2009, 17:55
Цитата(kashka @  17.4.2009,  13:02 Найти цитируемый пост)
Так как передаётся не сама ссылка а её копия, хм...Так это что же получается нет никакой другой возможности кроме как return добавлять?  


как насчет варианта с враппером?

Код

class ArrayWrapper{
private Object[][]array;
//getter.. setter
}


..

ну и метод транспонирования будет получать на вход объект класса ArrayWrapper
Код


trasponseArray(ArrayWrapper arrayWrapper);

Автор: math64 17.4.2009, 20:52
Если лень создавать враппер, сойдёт и массив из одного элемента:
Код

void trasponseArray(Object[][][] parray) {
  ...
}
...
Object[][] array;
...
Object[][][] parray = { array };
trasponseArray(parray);
array = parray[0];

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)