Re initialize a string array with no more memory left - java

In Java, since strings are immutable, when we re assign a string Array element to a different string and are out of memory would it compile and run fine?
my understanding is for example if there are 2 elements in a string array, "John" and "Henry", when i change the array's second element to "Tom", what happens to "Henry" since it cant be really over written (immutable strings) and behind the scenes is java pointing to a new location (should array locations not be next to each other? ).
I ran a test and it successfully changed the second element to Tom. It compiled and ran fine. As per my understanding this should not have been allowed because strings cant be over written and array are supposed to be consecutive memory locations. please clarify - thanks

Each cell of your array contains the address of a String instance. When you change the value of the second cell it just points to another String instance, and if the instance previously pointed by that cell is no longer pointed by any other variable, might be chosen for garbage collection.

Related

Java - A loop in a loop and the format gets lost

I'm French so excuse my English not necessarily correct.
I explain the context, I currently have a String array list named "tempCustomerDrugsIdsList" (var1) and another string array list named "tempDrugsTableList"(var2).
When I make a loop "For" on "var1" then another one in "var2","var2" loses its format, i. e. upper case is replaced by lower case and spaces are deleted.
I tested with another loop with the same type of variables (but empty), the result being the same I think the problem comes from my way of using java. Being on vb. net before, I must have taken some bad habits !
I don't know how to solve this problem, I've only been working in java for 2 weeks.
Thank you for helping me.
[EDIT]
My problem was:
List<String[]> tempDrugsTableList = otherList;
But this code doesn't duplicate the list.
AxelH gave me the following solution:
List<String[]> tempDrugsTableList = new ArrayList<String[]>(otherList);
Well, you are not doing a "copy" of the list
tempDrugsTableListCopy = tempDrugsTableList; // Get copy of original tempDrugsTableList for comparate
but sharing the reference, every update done in the tempDrugsTableListCopy will be done in the original list (same reference, same adress in memory). Since you are updating that copy in the following loops ... you update the original list too. What you want is to clone the list.
You could do it simply with copyList = new ArrayList(originalList); or for a deep clone, you need to iterate each element to duplicate those. (array need to be duplicated too if you change the value in those)
"String[]" tmpCustomerIds means you are getting a string array from a string array, which you would be using in a 2d array. Try it with just "String" in the for each loops. I am assuming you are using 1d arrays in this case.

Use of 'new' for arrays in Java

I've been undertaking some basic tutorials. One of which has asked me to set up an array to hold the following string values:
Beyonce (f)
David Bowie (m)
Elvis Costello (m)
Madonna (f)
Elton John (m)
Charles Aznavour (m)
Write a program to loop round, count how many are male vocalists and how many are female, and display my answer in the console.
I managed to complete it, but the way I set up my array was different to the answer provided.
Mine is as follows:
String names[] = {"Beyonce (f)", "David Bowie (m)", "Elvis Costello (m)", "Madonna (f)", "Elton John (m)", "Charles Aznavour (m)"};
And the provided answer is as such:
String[] singers = new String[6];
singers[0] = "Beyonce (f)";
singers[1] = "David Bowie (m)";
singers[2] = "Elvis Costello (m)";
singers[3] = "Madonna (f)";
singers[4] = "Elton John (m)";
singers[5] = "Charles Aznavour (m)";
Should I be creating a "new" array? If so, why? And what is the difference between the two?
Your answer is equivalent but more readable and less error-prone because you don't need any "magic numbers" for each array element with the "fear" of accessing an element out of the array definition and therefore creating an IndexOutOfBoundsException.
Both are doing the same thing.
First approach is more dynamic. You are telling java compiler that those elements will create an array. Compiler knows in compilation time their length, so it creates an array to fit them all.
In your second attepmt, you are first creating an array with length of 6. And then put in every slot one object.
When to use them:
If you know from the start what elements will be in the array, use first aproach - it is cleaner, shorter
But if there is some sort of logic that determined who should be placed in each slot, then second would be better. Like, when you want to create a 10 size array, but you will fill it during the runtime.
First aproach is also safer, because compiler created the array based on input length. Adding new element in code will change array size. In the second aproach you would have to change size manually, or ArrayOutOfBoundException will be thrown when adding singers[6].
But if you don't know the length of array (you will fill list in runtime), then you must use List or other dynamic structure (Set, List)
Both are valid ways to initialize an array.
Your way of initializing the array can only be done in the same expression that defines the array variable, though it can later be done in a similar way :
String[] names = null;
names = new String[] {"Beyonce (f)", "David Bowie (m)", "Elvis Costello (m)", "Madonna (f)", "Elton John (m)", "Charles Aznavour (m)"};
The provided answer explicitly creates an array of Strings, specifies the number of Strings that this array can store, and assigns values to the indices of the array.
Both solutions are correct. The first one uses an array initializer, the second one first instantiates the array and then populates it with values. One could argue that the first solution is more robust, since in the second solution the length of the array must be explicitly given before the entries are provided and it is possible to use indices beond the capacity of the array, which will be only detected at runtime.
A new array can be created with the new operator followed by the array element type and the array size between the [ ] characters - this is called the Array Creation Expression. Or when you declare a variable, you can also use an array literal which is called Array Initializers (but this cannot be used to assign a value to an array later on just when it is declared).
When you write
String names[] = {"A", "B", "C"};
It is just a short form and is equivalent to
String[] names = new String[] {"A", "B", "C"};
Note that to indicate that an array of the specified type is to be declared, both String[] names and String names[] forms can be used and they are equivalent, altough:
String names[], names2; // Only names is an array, names2 is just a String
While
String[] names, names2; // Both names and names2 are arrays!
It is recommended to use the second form to avoid confusion and accidents if later you add more variable names to the declaration.
Let's take a closer look of what happens in the 2 cases:
// "Slow" filling
String[] names = new String[3]; // Filled with null values by default
names[0] = "A"; // Explicit index, index range check!
names[1] = "B"; // Explicit index, index range check!
names[2] = "C"; // Explicit index, index range check!
// "Fast" filling at creation time, implicit indices and array length,
// No index checks!
String[] names = {"A", "B", "C"};
Advantages of creating and initializing the array in one step
There are several advantages of creating and initializing an array in one step:
Doing so is less error-prone: the compiler will determine the length of the array, and also the compiler will initialize the elements of the array based on the list you provide. No such thing as using a wrong index value or getting an ArrayIndexOutOfBoundsException.
It will be faster because the JVM (Java Virtual Machine) will not initialize the array with null values.
It will be faster because you don't have to specify explicit index values and the JVM does not have to work with them. Also the JVM does not have to check if indices are in the valid range (that is 0..length-1).
Less maintenance/overhead when later you want to add another element in the middle of the array, you just have to insert it where you want it. Would you have initialized your array the other way, you would have to update all subsequent indices.
The Java source code will be shorter, more compact and also the compiled byte code will be shorter.
You can read more about arrays in the Java Language Specification: Chapter 10. Arrays
The 'new' operator in java is responsible for the creation of new object or we can say instance of a class.
Actually, it dynamically allocates memory in the heap with the reference we define pointed from the stack.

Creating unnecessary string objects

This is a bit of Java String 101. I came across this recently in some existing code. My initial reaction is that this is redundant
car.setDetails(new String(someStringBufferObj.toString));
In my opinion even this would be redundant...
car.setDetails(new String(someOtherStringObj));
because String is immutable so there is never a risk that the car details would be changed accidentally (by changing someOtherStringObj) in a later line of code
Are am I wrong?
The first snippet above looks unnecessary. However the second may be necessary. Consider the following.
The constructor String(String) is useful since it'll take a copy of the underlying character array of the original string.
Why is this useful ? You have to understand that a string object has a character array underlying it, and getting a substring() of an existing string actually uses that original character array. This is a flyweight pattern. Consider the following
String s = longstring.substring(2,4);
The string s points to the character array underlying longstring (somewhat unintuitively). If you want to bin longstring (using garbage collection) the underlying character array won't be binned since s still references it, and you end up consuming a potentially huge amount of memory for a 2 character string.
The String(String) constructor resolves this by creating a new character array from that referenced by the string being used to construct from. When the original string is removed via garbage collection its character array won't be referenced by the substring() result and hence that will be removed too.
Note that this behaviour has changed very recently in Java (release 7u4, I think) and strings don't support the above mode of operation anymore.
You're absolutely right Rob, there's no need to new up a String in this instance. Just providing a call to someStringBufferObj.toString() should be sufficient!

How do I make an array from inputted information (i.e. names) and then use it as objects within the code?

I've been reading up on it, but every question I've found has asked for slightly different things, such as only wanting a single letter for their array, or in a different language (I'm new and only learning java at the moment), so here I am.
I want to set up an array that uses the user's input for their names.
What I have so far is this, I'm assuming this is the declaration line, where later I use an input line to define a value within the array (which I also am unsure how to do)
String[] array = {"name"};
But I don't know how to for example print.out the object or keep up with which name will be what value. I appreciate your time taken to teach me!
EDIT for further clarification. I'm trying to write up a small app that asks the user for numerous names, addresses, and phone numbers (Type name -> Type name's address -> type name's phone number, ask if they want to add another person, if yes then go back to asking for another name)
I am unsure how to set up a String array or how to use it throughout. However, thanks to your input and coming back after some fresh air, I have a better idea how to word it for google. Thank you guys for your help, even if it was just to gesture a better articulated question.
An array is a sequence of values. You have created an array of Strings that is one String long. To access the value at a specific of an array, use array subscript notation: the name of the array followed by a pair of square brackets ([]) with the index in between them.
String[] anArrayOfStrings = {"string0", "string1", "string2"};
anArrayOfStrings[0]; //the first element
System.out.println(anArrayOfStrings[1]); //print the second element
anArrayOfStrings[2] = "new string value"; //assign the third element to a new value
if (anArrayOfStrings[0].equals("string0") //evaluate the first element and call a method
{
//this block will execute anArrayOfStrings[0] is "string0"
}
anArrayOfStrings[3]; //error, index out of bounds
Simply declaring the array would be
String[] names;
In your code you both declare and assign it in the same line by using an initializer list.
To assign individual elements, use the [] notation. Note that once you initialized you list to be only one String long, it cannot become longer than without be re-assigned. To declare an array of any size, you can use:
String[] arrayWithInitialSize = new String[5]; //holds five strings, each null to begin with

Why does appending "" to a String save memory?

I used a variable with a lot of data in it, say String data.
I wanted to use a small part of this string in the following way:
this.smallpart = data.substring(12,18);
After some hours of debugging (with a memory visualizer) I found out that the objects field smallpart remembered all the data from data, although it only contained the substring.
When I changed the code into:
this.smallpart = data.substring(12,18)+"";
..the problem was solved! Now my application uses very little memory now!
How is that possible? Can anyone explain this? I think this.smallpart kept referencing towards data, but why?
UPDATE:
How can I clear the big String then? Will data = new String(data.substring(0,100)) do the thing?
Doing the following:
data.substring(x, y) + ""
creates a new (smaller) String object, and throws away the reference to the String created by substring(), thus enabling garbage collection of this.
The important thing to realise is that substring() gives a window onto an existing String - or rather, the character array underlying the original String. Hence it will consume the same memory as the original String. This can be advantageous in some circumstances, but problematic if you want to get a substring and dispose of the original String (as you've found out).
Take a look at the substring() method in the JDK String source for more info.
EDIT: To answer your supplementary question, constructing a new String from the substring will reduce your memory consumption, provided you bin any references to the original String.
NOTE (Jan 2013). The above behaviour has changed in Java 7u6. The flyweight pattern is no longer used and substring() will work as you would expect.
If you look at the source of substring(int, int), you'll see that it returns:
new String(offset + beginIndex, endIndex - beginIndex, value);
where value is the original char[]. So you get a new String but with the same underlying char[].
When you do, data.substring() + "", you get a new String with a new underlying char[].
Actually, your use case is the only situation where you should use the String(String) constructor:
String tiny = new String(huge.substring(12,18));
When you use substring, it doesn't actually create a new string. It still refers to your original string, with an offset and size constraint.
So, to allow your original string to be collected, you need to create a new string (using new String, or what you've got).
I think this.smallpart kept
referencing towards data, but why?
Because Java strings consist of a char array, a start offset and a length (and a cached hashCode). Some String operations like substring() create a new String object that shares the original's char array and simply has different offset and/or length fields. This works because the char array of a String is never modified once it has been created.
This can save memory when many substrings refer to the same basic string without replicating overlapping parts. As you have noticed, in some situations, it can keep data that's not needed anymore from being garbage collected.
The "correct" way to fix this is the new String(String) constructor, i.e.
this.smallpart = new String(data.substring(12,18));
BTW, the overall best solution would be to avoid having very large Strings in the first place, and processing any input in smaller chunks, aa few KB at a time.
In Java strings are imutable objects and once a string is created, it remains on memory until it's cleaned by the garbage colector (and this cleaning is not something you can take for granted).
When you call the substring method, Java does not create a trully new string, but just stores a range of characters inside the original string.
So, when you created a new string with this code:
this.smallpart = data.substring(12, 18) + "";
you actually created a new string when you concatenated the result with the empty string.
That's why.
As documented by jwz in 1997:
If you have a huge string, pull out a substring() of it, hold on to the substring and allow the longer string to become garbage (in other words, the substring has a longer lifetime) the underlying bytes of the huge string never go away.
Just to sum up, if you create lots of substrings from a small number of big strings, then use
String subtring = string.substring(5,23)
Since you only use the space to store the big strings, but if you are extracting a just handful of small strings, from losts of big strings, then
String substring = new String(string.substring(5,23));
Will keep your memory use down, since the big strings can be reclaimed when no longer needed.
That you call new String is a helpful reminder that you really are getting a new string, rather than a reference to the original one.
Firstly, calling java.lang.String.substring creates new window on the original String with usage of the offset and length instead of copying the significant part of underlying array.
If we take a closer look at the substring method we will notice a string constructor call String(int, int, char[]) and passing it whole char[] that represents the string. That means the substring will occupy as much amount of memory as the original string.
Ok, but why + "" results in demand for less memory than without it??
Doing a + on strings is implemented via StringBuilder.append method call. Look at the implementation of this method in AbstractStringBuilder class will tell us that it finally do arraycopy with the part we just really need (the substring).
Any other workaround??
this.smallpart = new String(data.substring(12,18));
this.smallpart = data.substring(12,18).intern();
Appending "" to a string will sometimes save memory.
Let's say I have a huge string containing a whole book, one million characters.
Then I create 20 strings containing the chapters of the book as substrings.
Then I create 1000 strings containing all paragraphs.
Then I create 10,000 strings containing all sentences.
Then I create 100,000 strings containing all the words.
I still only use 1,000,000 characters. If you add "" to each chapter, paragraph, sentence and word, you use 5,000,000 characters.
Of course it's entirely different if you only extract one single word from the whole book, and the whole book could be garbage collected but isn't because that one word holds a reference to it.
And it's again different if you have a one million character string and remove tabs and spaces at both ends, making say 10 calls to create a substring. The way Java works or worked avoids copying a million characters each time. There is compromise, and it's good if you know what the compromises are.

Categories