This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 7 years ago.
Java supports pass by value (always works on a copy) but when you pass a user defined object then it changes the actual object (kind of pass by reference but no pointer changes), which I understand but why the changeObject2CLEAR method below is actually changing the value of the object ? Instead it has to work on the copy?
import java.util.HashMap;
import java.util.Map;
public class PassBy {
class CustomBean {
public CustomBean() {
}
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return id + ", " + name;
}
}
public Map<Integer, String> changeObject2NULL (Map<Integer, String> m) {
m = null;
return m;
}
public Map<Integer, String> changeObject2CLEAR (Map<Integer, String> m) {
m.clear();
return m;
}
public CustomBean changeCustomObject (CustomBean _e) {
_e.setId(_e.getId() + 1);
return _e;
}
public static void main(String[] args) {
PassBy passby = new PassBy();
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "value");
CustomBean bean = passby.new CustomBean();
bean.setId(1);
bean.setName("arun");
// Initial Value
System.out.println(map.toString());
System.out.println(bean.toString());
System.out.println("-------------------------");
// Pass by value works fine
passby.changeObject2NULL(map);
passby.changeCustomObject(bean);
// Custom object value changes since we pass as the object reference but Map remains with actual value
System.out.println(map.toString());
System.out.println(bean.toString());
System.out.println("-------------------------");
// Testing Pass by value - CANT UNDERSTAND why it changed the object since it has to be pass-by-value and not by ref in this case ??
// Why setting to null not chainging the value but clear does ?
passby.changeObject2CLEAR(map);
System.out.println(map.toString());
}
}
So let me try to help you understand, Java always does pass by value, but I am sure you know that all object instances are actually pointers to those objects. Now when you send an object then you are passing a value of the address of the object. If you do any changes to the object itself (like m.clear()) then it goes to that address, type casts the object and does the operation on it. But if you change the pointer itself, like m = null, then only the copy of the address you are holding is changed.
When you call changeObject2CLEAR
passby.changeObject2CLEAR(map);
you are passing the instance map.
in the method changeObject2CLEAR
public Map<Integer, String> changeObject2CLEAR (Map<Integer, String> m) {
m.clear();
return m;
}
you perform .clear() on that same instance map even though in the method it is called m.
As an exercise in understanding notice that the following method will do the same thing.
public void changeObject2CLEAR (Map<Integer, String> m) {
m.clear();
}
Notice that you don't have to return the Map<Integer, String> m because the map you have access to is the same instance object passed in wherever the method is called.
EDIT: Why does m = null; behave as pass-by-value but m.clear() behave as pass by reference?
When you 'assign' the value null to m you are changing the reference from the previous instance object map to a new memory location that is null.
When you call the .clear() method on the instance object m you are calling the method on the same object that is at the memory location referenced by map, consequently you modify the map object.
AFAIK Java only does passes by value, but the values are actually references
Related
I discovered when saving a POJO with a map field using Firebase on Android, that if that map contains nulls in the value property of that map, then the whole field is ignored.
The workaround is easy (non-null values will result in the map saving successfully), but I want to understand why is this so?
Model
public class Game {
private String owner;
private Map<String, String> characters;
public Game() {
// empty constructor for Firebase
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public Map<String, String> getCharacters() {
return characters;
}
public void setCharacters(Map<String, String> characters) {
this.characters = characters;
}
}
Calling code
final Game game = new Game();
game.setOwner("owner");
game.setCharacters(new HashMap<String, String>());
game.getCharacters().put("Bugs Bunny", null);
game.getCharacters().put("Batman", null);
final Firebase ref = new Firebase("<firebaseurl>/test/" + System.currentTimeMillis());
ref.setValue(game);
Resulting object in Firebase
{
"1456421340781": {
"owner": "owner"
}
}
They're actually not ignored. When you give Firebase a null value for a property/path, you indicate that you want to property or path to be deleted.
From the documentation on Firebase's JavaScript set() method:
Passing null for the new value is equivalent to calling remove(); all data at this location or any child location will be deleted.
So if you set a value with:
ref.child("keith").setValue(47649);
Then the following will delete it:
ref.child("keith").setValue(null);
This behavior is most useful when you use updateChildren(), but it works equally when you call setValue().
A method in BayesianClassifier calls the method below (a method of Category):
public void updateProbabilities(Map<String, int> woordfrequenties) {
for (Map.Entry<String, int> woordfrequentie : woordfrequenties.entrySet()) {
String woord = woordfrequentie.getKey();
int frequentie = woordfrequentie.getValue();
int index = BayesianClassifier.getVocabulary().indexOf(woord);
}
}
Now, it states that the non-static method getVocabulary from BayesianClassifier cannot be referenced from the static context here, which I understand, but how then can the method get the value of field 'vocabulary' from the instance of BayesianClassifier that calls this method? It surely must be possible without passing the whole vocabulary as a parameter, or giving the class Category the instance of BayesianClassifier as a field?
As stated in comments it can be done with Reflection. If you cannot pass vocabulary as a parameter then go with the answer here: Previous Answer to Same Question
If you do not want to use Reflection you have two options:
Option 1:
Just pass in the Vocabulary object when calling updateProbabilities. Java is pass-by-value, however this does not mean that the entire Vocabulary object will be copied and passed. This is explained here. Essentially, the value of the pointer to your object will be passed, and in doing so only take up the extra space of one "pointer".
Option 2:
When creating your Category object, add the BayesianClassifier as a field.
You can use it's using following code:
public class Category {
private final BayesianClassifier bayesianClassifier;
public Category(BayesianClassifier bayesianClassifier) {
this.bayesianClassifier = bayesianClassifier;
}
public void updateProbabilities(Map<String, int> woordfrequenties) {
for (Map.Entry<String, int> woordfrequentie : woordfrequenties.entrySet()) {
String woord = woordfrequentie.getKey();
int frequentie = woordfrequentie.getValue();
int index = bayesianClassifier.getVocabulary().indexOf(woord);
}
}
or
public class Category {
private BayesianClassifier bayesianClassifier;
public void setBayesianClassifier(BayesianClassifier bayesianClassifier) {
this.bayesianClassifier = bayesianClassifier;
}
public void updateProbabilities(Map<String, int> woordfrequenties, BayesianClassifier bayesianClassifier) {
for (Map.Entry<String, int> woordfrequentie : woordfrequenties.entrySet()) {
String woord = woordfrequentie.getKey();
int frequentie = woordfrequentie.getValue();
int index = bayesianClassifier.getVocabulary().indexOf(woord);
}
}
or
public void updateProbabilities(Map<String, int> woordfrequenties, BayesianClassifier bayesianClassifier) {
for (Map.Entry<String, int> woordfrequentie : woordfrequenties.entrySet()) {
String woord = woordfrequentie.getKey();
int frequentie = woordfrequentie.getValue();
int index = bayesianClassifier.getVocabulary().indexOf(woord);
}
}
I have to use a map which stores keys of type Integer, String and Long only.
One solution: To store type Object and in put method check with instanceof operator. Is there any better solution, maybe with enum
You can use a map and storing Long as String into it
or you can use two different hashmap and duplicate put/get methods. If you have two types, it is probably for two different things, and having two different map should probably be the correct answer
Create a class that has a map as a member and add methods that will store and retrieve int and long as Strings.
class MyMap {
private Map mabObject = Map<String, Object>;
public void add(long key, Object value) {
mapObject.put(Long.toString(key),value);
}
public void add(String key, Object value) {
mapObject.put(key, value);
}
public Object get(long key) {
return mapObject.get(Long.toString(key));
}
public Object get(String key) {
return mapObject.get(key);
}
}
I agree with Paul Boddington's comment, and the need of such trick shows that code smells.
Just for a funny excercise (not for production code) I've made an example that shows what we can do in compile time for limiting types of keys in a map.
For example we can create a wrapper allowing only values of specific classes.
common/map/Wrap.java
package common.map;
import java.util.Arrays;
import java.util.List;
public class Wrap<T> {
private T value;
private Wrap(T value){
this.value = value;
}
public T get() {
return this.value;
}
/*
* it's important to implement this method
* if we intend to use Wrap instances as map's key
*
* and it's needed to see that hash codes are computing differently in different classes,
* and depending on `allowedClasses` contents we can face some unexpected collisions
* so if you care of performance - test your maps usage accurately
*/
public int hashCode() {
return this.value.hashCode();
}
/*
* static
*/
private static List<Class> allowedClasses = Arrays.asList(Long.class, String.class);
public static <T> Wrap<T> create(Class<? extends T> clazz, T value) {
if (!allowedClasses.contains(clazz)) {
throw new IllegalArgumentException("Unexpected class " + clazz);
}
return new Wrap<>(value);
}
public static <T> Wrap<T> create(AllowedClasses allowedClass, T value) {
return create(allowedClass.clazz, value);
}
public enum AllowedClasses {
LONG(Long.class),
STRING(String.class);
private Class clazz;
AllowedClasses(Class clazz) {
this.clazz = clazz;
}
}
}
And let's run it
common/map/Example.java
package common.map;
import common.map.Wrap.AllowedClasses;
import java.util.HashMap;
import java.util.Map;
public class Example {
public static void main(String... args) {
Map<Wrap, Object> map = new HashMap<>();
// next two lines create wrappers for values of types we added to enum AllowedClasses
// but since enums cannot have type parameters, we are not able to check
// if the second parameter type is compatible with a type associated with given enum value
// so I think usage of enum is useless for your purpose
Wrap<?> valLong0 = Wrap.create(AllowedClasses.LONG, "the string in place of Long is OK");
Wrap<?> valString0 = Wrap.create(AllowedClasses.STRING, 12345);
// from the next lines you can see how we can use the Wrap class to keep
// only allowed types to be associated with the map keys
Wrap<Long> valLong = Wrap.create(Long.class, 1L); // legal
Wrap<String> valString = Wrap.create(String.class, "abc"); // legal
Wrap<String> valWrong = Wrap.create(String.class, 123); // doesn't compile
Wrap<Object> valWrong2 = Wrap.create(Object.class, 123); // compiles but throws exception in runtime
Object obj = ThirdParty.getObjectOfUnknownClass();
Wrap<?> valDynamic = Wrap.create(obj.getClass(), obj); // compiles but MAYBE throws exception in runtime
// so we get to this point only if all the wrappers are legal,
// and we can add them as keys to the map
map.put(valLong, new Object());
map.put(valString, new Object());
map.put(valDynamic, new Object());
}
}
HashMap<DataType1,DataType2>hm = new HashMap<DataType1,DataType2>();
or
Map<DataType1,DataType2> m = new HashMap<DataType1,DataType2>();
m.put(key, value);
Instead of DataType1 & DataType2 you can add Integer,String,Long ,etc. and use the put(key,value) method to enter key and values into the HashMap.
Yes, I know that's impossible; but the problem is that I really need to do it. I'll explain the whole trap:
public class MainMethods {
ArrayList arrayOfValues; // << PROBLEM
HashMap<String, Object> matrix = new HashMap<String, Object>();
void sendKeyToMatrix(String key) {
arrayOfValues = new ArrayList();
matrix.put(key, arrayOfValues);
}
void attachValueToKey(Object object, Object value) {
matrix.put((String) object, arrayOfValues.add(value));
}
void removeFromMatrix(String key) {
matrix.remove(key);
}}
That's my class and those are my methods. I created a HashMap with the key being a String and its value being an Object; pretty simple.
The real problem is with what I intend to do with this "Object" as a value. I have a GUI and a button that calls the "sendKeyToMatrix", and another one that attach a value to it, both from textFields. Since the ".put()" method for HashMaps requires an Object and I must create only the key first, the second argument is "null" or THE PROBLEM (ArrayList).
The perfect solution:
public class MainMethods {
HashMap<String, Object> matrix = new HashMap<String, Object>();
void sendKeyToMatrix(String key) {
ArrayList arrayOfValues = new ArrayList();
matrix.put(key, arrayOfValues);
}
void attachValueToKey(Object object, Object value) {
matrix.put((String) object, ghostOrigin.add(value));
}
void removeFromMatrix(String key) {
matrix.remove(key);
}}
When I call the "sendKeytoMatrix" with the button, it creates a key with an empty ArrayList as its value. This key is added to my JList. Then, when I call the second button (Considering what is selected in the JList), I add an element to the ArrayList:
Code for the second button:
btnInsertContent.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
mainMethods.attachValueToKey(mainList.getSelectedValue(), textContent.getText());
mainCombo.addItem(textContent.getText());
System.out.println(mainMethods.matrix);
}
});
The second argument of the "attachValueToKey" receives the String given by the textField, and I reach the big problem:
I can't add it to the ArrayList inside the "sendKeyToMatrix" method, which is obvious, but that's a big problem, because if I declare the variable as a field up there (So that I can access it down in the other scope), I get wrong and esoterically misterious results that are unknown to me.
Resuming this in a simple question: How to access the variable inside the other method?
If I declare the variable inside the "attachValueToKey", it will create an ArrayList inside of the ArrayList every time the button is pressed.
Well, I thank you all for the help. Probably there must be a way to summon the solution through Object Oriented Magic, with instances and the like.
Something like this should answer your question I suppose:
void attachValueToKey(Object object, Object value) {
ArrayList a = matrix.get((String) object);
a.add(value));
}
For learning purposes, I'll leave here the solution I found thanks to Lazarus:
public class MainMethods {
HashMap<String, Object> matrix = new HashMap<String, Object>();
void sendKeyToMatrix(String key) {
ArrayList<Object> arrayOfValues = new ArrayList<Object>();
matrix.put(key, arrayOfValues);
}
void attachValueToKey(Object object, String value) {
ArrayList<String> arrayInMatrix = (ArrayList<String>) matrix.get(object);
arrayInMatrix.add(value);
matrix.put((String) object, arrayInMatrix);
}
void removeFromMatrix(String key) {
matrix.remove(key);
} }
Action of the Button:
btnInsertContent.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
mainMethods.attachValueToKey(mainList.getSelectedValue(), textContent.getText());
mainCombo.addItem(textContent.getText());
System.out.println(mainMethods.matrix);
}
});
This question already has answers here:
How to return multiple objects from a Java method?
(25 answers)
Closed 9 years ago.
I'm trying to do something like this:
public void <String,int> getItem
{
return <"Jen",23>;
}
I know I can use a custom class, but how I would I return two results in one function call.
1 - Is the above template function possible in java, and how would by caller get part 1 of it and part 2 later.
2 - Can I do it using an associative array like in actionscript?
3 - Can I do it using a hashmap of some sort?
4 - What are other possible ways are there
I attempted all three ways, but one way or another syntax is hitting me. So if anyone can give clear examples
Java functions always return a single value, so your only option is to return a "collection" object which contains multiple values, such as an Array or a proper Collection. For example:
public Object[] getItem() { return new Object[] { "Jen", 23 }; }
public Collection<Object> { return Arrays.asList("Jen", 23); }
Although, a typical pattern in Java is to return a custom type which encapsulates your values, e.g.:
public class NameAge {
public final String name;
public final int age;
public NameAge(String name, int age) {
this.name = name;
this.age = age;
}
}
// ...
public NameAge getItem() { return new NameAge("Jen", 23); }
Or more generally:
public class Pair<X, Y> {
public final X x;
public final Y y;
public Pair(X x, Y y) {
this.x = x;
this.y = y;
}
}
// ...
public Pair<String,Integer> getItem() {
return new Pair<String,Integer>("Jen", 23);
}
Of course, there are serious implications regarding hashing (equality and hash code) if you want to use these custom types as hash keys.
I like using generics! Create your own class and return an instance of it:
public class Tuple<T,V>
{
public T item1;
public V item2;
public Tuple(T i1, V i2)
{
item1 = i1;
item2 = i2;
}
}
Then you create your method:
public Tuple<String, int> getItem()
{
return new Tuple<String, int>("Jen", 23);
}
Java does not allow for multiple return statements. The best practice I believe is to create a custom object. What you have here suggests some sort of Person class, a la
public class Person {
int Age;
String Name;
}
Returning an object will make it more intuitive what you are doing as well.
You can return a Bundle.
public Bundle getItem(){
Bundle theBundle = new Bundle();
theBundle.putString("name","Jen");
theBundle.putInt("age",23);
return theBundle;
}
Usually, if you need to return two values from one function - it's a code smell. Try to refactor your code so that every function always return just one value. Keep in mind that no return value (void) is also a code smell, but less critical.
The proper way would be to create a class for your return set:
public class ReturnSet {
private String str;
private int num;
public ReturnSet(String _str, int _num) {
str = _str;
num = _num;
}
//add getters and setters
...
}
Then your function would look like
public ReturnSet getItem() {
...
return new ReturnSet(strValue, intValue);
}
Of course, you can fudge things by having your function return an array of Object, but this would be a rather bad code:
public Object[] getItem() {
Object[] result;
//allocate it, get data;
...
result[1] = strValue;
relult[2] = new Integer(intValue);
return result;
}
You can even return a hashmap with one element in it:
public Map getItem() {
Map result;
//allocate it, say as hashmap, get data;
...
result.put(strValue, new Integer(intValue));
return result;
}
Then in the caller, the key of the map would be the first part and the value would be the second.
While there are may be many ways of doing things like that, the first one is the right approach.
If a method returns something, then its return type must be this something:
public MyCustomObject getItem();
or
public Object[] getItem():
or anything else wher you can store the results.
But Java is a statically typed OO language. A custom class is the way to go.
You can also return one value the regular way and other(s) by using a "return" parameter:
class C {
Type2 value; // omitted getter and setter for brevity
}
Type1 f1(C returnParameter, String otherParameters...)
{
// function body here
returnParameter.value=returnValue2; // store the second result
return returnValue1; // return the first result
}
// usage
Type1 result1;
Type2 result2;
C helper = new C();
result1=f1(helper, "foo", "bar");
result2=helper.value;
For more results either use several "helper" objects or one that can hold several values.
I am myself looking for a most elegant solution (in my case one return type is a Collection and the other is an integer number-any variant of it is OK).