A newly created ArrayList without adding any elements to it looks just like an ArrayList with an empty string added in it.
This makes me feel that a newly created ArrayList even without adding any elements in it has a default empty string or element added in it.
package cracking;
import java.util.ArrayList;
public class Ask
{
public static void main(String[] args)
{
ArrayList<String> al = new ArrayList<String>();
System.out.println(al.size());//0
System.out.println(al);//[]
al.add("");
System.out.println(al.size());//1
System.out.println(al);//[]
}
}
However, the discrepancy between the size of ArrayList in the two scenarios makes them inconsistent, especially since the two ArrayList when printed out look just the same.
To maintain consistency, I feel, either of two would've been good:
The size of the ArrayList which is just created, i.e., even before
adding any element to it should show 1 implying empty string or
element, since even adding to the ArrayList makes it look the same.
Printing out a newly created ArrayList should not be allowed, it
should just print NULL or something instead of showing [] like it
shows now
You are being tricked by the toString implementation of the AbstractCollection. See here:
public String More ...toString() {
Iterator<E> i = iterator();
if (! i.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = i.next();
sb.append(e == this ? "(this Collection)" : e);
if (! i.hasNext())
return sb.append(']').toString();
sb.append(", ");
}
}
Your collection has a size of 1, but it's not printing anything, including the comma character , because it only has size one. Try adding multilpe emptry Strings and you'll see the result more clearly.
al.add("");
al.add("");
al.add("");
al.add("");
al.add("");
System.out.println(al); // Prints [, , , , ]
[] is representative of an empty list. This is by convention, but I think you'll find this reasonable.
And printing the ArrayList uses toString on each of the objects that it contains to show a printed representation of those contents. When a string is printed (using toString) the quotes aren't included.
Consider the following amendment to your code:
ArrayList<String> al = new ArrayList<String>();
al.add("");
al.add("");
al.add("");
System.out.println(al);//[]
The output is:
[, , ]
We might just as well complain about the use of the brackets when printing an ArrayList, but note, that the output of toString is often only useful for debugging, and shouldn't be relied on programatically.
From the docs for toString:
In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read.
In the case of your example, this does break down a bit: printing the ArrayList<strin> with a single empty string isn't particularly informative. If you need to check/validate the contents of your collection, e.g. for debugging, you should also print the length.
ArrayList is using the toString of the object that you place into it as part of its toString call. The reason this appears odd to you is due to the fact that printing an empty string prints nothing.
Ideally, one would not elect to store an empty string in the list, and would instead do a check to ensure that only non-empty strings ever made their way into it.
if(!"".equals(value)) {
al.add(value);
}
Related
Say I have Map<List<String>, List<String>> whatComesNext,
And while in a for loop, for every iteration I want to add the nth element of List<String> text to the value of whatComesNext. Why can I not perform whatComesNext.put(key, whatComesNext.get(key).add(text.get(n)))? The idea would be to retrieve the value from its respective key in the hashmap and add my desired String to it. This is assuming that every key in the hashmap has a value.
Below is my full code:
static void learnFromText(Map<List<String>, List<String>> whatComesNext, List<String> text) {
for (int i=0; i<=text.size()-3; i++) {
if (whatComesNext.containsKey(Arrays.asList(text.get(i),text.get(i+1)))==false) {
whatComesNext.put(Arrays.asList(text.get(i),text.get(i+1)), Arrays.asList(""));
}
whatComesNext.put(Arrays.asList(text.get(i),text.get(i+1)), whatComesNext.get(Arrays.asList(text.get(i),text.get(i+1))).add(text.get(i+2)));
}
}
The Arrays.asList() looks complicated, but it's because I was getting null maps when trying to intialize my own String Lists to try and hold my keys and values, which someone told me was because I was repeatedly clearing the lists that the keys & values were assigned to, leaving them null. I thought I'd solve that problem by referring directly to the original List<String> text, because that remains unchanged. The idea is to first check if a key is not present in the map, and if so assign it an empty List as a value, and then add a String from text to the value of the map.
The error I get when running the code is Error: incompatible types: boolean cannot be converted to java.util.List<java.lang.String> in the line whatComesNext.get(Arrays.asList(text.get(i),text.get(i+1))).add(text.get(i+2)));. I don't understand where this could go wrong, because I don't see which method is returning a boolean.
The error comes from the fact that List.add(Object o) returns a boolean and not the List itself. The Map is declared to contain instances of List<String> as value. If you simply want to add a value to a list, just retrieve it from the map and call add on it. Check the result of the get-process for null and create a new list and put it into the Map if that's the case
I can see a couple of other problems as well:
You call Arrays.asList(...) multiple times creating multiple lists with the same elements. This is a major performance issue and you're just lucky, that the returned list is actually implementing equals, so that your logic is actually working (I expected that to be the problem of your "doesn't work"-description before you updated it.
If the key doesn't exist, you're creating a List containing an empty text. If that should be an empty list, that's not what you're doing and you might run into problems later on, when you work with text-values (that is the empty text as first element) that weren't part of the original input values.
Without changing the type of the key of the Map a - in my eyes - better implementation would look like this:
static void learnFromText(Map<List<String> whatComesNext, List<String>, List<String> text) {
for (int i=0; i<= text.size() - 3; i++) {
List<String> listKey = text.subList(i, i+2);
List<String> value = whatComesNext.get(listKey);
if (value == null) {
value = new ArrayList<>();
whatComesNext.put(listKey, value);
}
value.add(text.get(i+2)));
}
}
The calculation of the list for the keys happens only once, increasing performance and reducing the need of resources. And I think it's more readable that way as well.
The .add() method returns a boolean, your parenthesis are misplaced, replace your last line with this one:
whatComesNext.put(Arrays.asList(text.get(i),text.get(i+1)), whatComesNext.get(Arrays.asList(text.get(i),text.get(i+1)))).add(text.get(i+2));
I'm really good with VB and I have a project where I need to check an array. If the same item in an array exists twice or more it needs to be changed to an item that doesn't exist. Now I'm in a class where they're making us use Java for this project.
I was wondering what is the equivalent of a for each loop in Java? I checked the JavaDocs and it only had info for the regular for loop, I didn't notice any section that said anything about a for each loop.
It's more subtle in Java than VB. You can find the official docs in the Oracle documentation here (towards the bottom):
Java For Loops
The provided example is:
// Returns the sum of the elements of a
int sum(int[] a) {
int result = 0;
for (int i : a)
result += i;
return result;
}
Hope that helps. Be careful not to remove or add elements inside the loop or you will get a Concurrent Modification Exception.
try
String arr [] = // you decide how this gets initialized
for (String obj: arr) {
}
This is called "iterating over collections". An array can be implicitly converted to a collection, so you can iterate over an array in the same way, using the "enhanced for-loop".
List<String> names = new LinkedList<>();
// ... add some names to the collection
for(name:names) {
System.out.println(name);
}
I'm not sure if VB has collections - they are a big part of Java and I recommend you look into them.
Of course this changes a bit in Java 8, although you'll notice a collection is still the backbone of forEach().
List<String> names = new LinkedList<>();
// ... add some names to the collection
names.forEach(name -> System.out.println(name));
A for each loop (also known as the enhanced for loop) is as follows:
for (String name : names) {
// here, the loop will work over each element of 'names',
// with the variable name with which to access each element
// being 'name', and output it
System.out.println(name);
}
A normal for loop is as follows:
for (int i = 0; i < max; i++) {
// here, i will iterate until max, then the loop will stop.
// any array access here has to be done manually using i, which increments.
}
If insertion order from the names array is important, keep adding the objects to a LinkedHashSet<String>, then with either a for loop or enhanced for loop or iterator, go over your list of names and add each of them to the LinkedHashSet. If the add method, passing in your name, returns false, generate a new name and add that.
If insertion order is not important, use a HashSet<String> instead.
At the end, convert back to an array if it is important (String[] bla = map.toArray(new String[0])), or output the toString() of the map.
This question already has answers here:
How do I print my Java object without getting "SomeType#2f92e0f4"?
(13 answers)
Closed 3 years ago.
In Java, I created a linked list class and added a bunch of values. But I don't know how to display those values.
I tried doing the following:
System.out.println(list);
But it printed out some weird values like ADTList# followed by some jibberish.
for(Object o : list){
System.out.println(o);
}
You will have to iterate through all the items in your linked list and print their values, or else, override the toString method for each of your items.
"ADTList#(followed by some jibberish)" is created by the default toString() method. Override this to print something useful. This may require overriding in both the list implementation and the elements.
Assuming that ADTList is your own implementation of a linked list, it's toString method could look something like this:
#Override
public String toString() {
Iterator<Object> i = iterator();
if (! i.hasNext()) {
return "[]";
}
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
Object element = i.next();
sb.append(e == this ? "(this Collection)" : element);
if (! i.hasNext()) {
return sb.append(']').toString();
}
sb.append(", ");
}
}
This has been copied from the JDK's AbstractCollection class, as Christoffer Hammarström mentioned in his comment. It adds characters to make it list-like, e.g. "[x,y,z]" and also handles the list containing itself.
It might also be worth mentioning that the 'gibberish' in the default toString does actually have meaning. It is the name of the class the object is an instance of, and the hexadecimal value of object's hashCode - where the default hashCode(), will represent a memory location.
You have to get an Iterator on that list by invoking iterator(). Then call next() to get the elements until hasNext() returns false.
Iterator<Object> i = list.iterator();
while(i.hasNext())
System.out.print(i.next()+"\t");
You need to iterate it to see the contents.
for(Object obj : list) {
System.out.println(obj);
}
But your Object needs to have .toString() overriden.
You can print each value performing a for each loop on the list :
List<String> list = new LinkedList<>();
//Fill the list
for (String s : list)
System.out.println(s);
If you have created your own implementation of a linked list (any reasons why you would do that?), you also need to override the default behavior of toString() if you want to print the content of the list in a user friendly way.
If you use the LinkedList provided in java.util, then the line of code you show in your question will print as expected.
Override the toString method and write your own implementation to print useful Info About the same.
public String toString(){
System.out.println("*** CONTENTS ***");
for(Object obj: this){
System.out.println(obj);
//May be you will be required to override toString in your custom domain class to make it more useful
}
System.out.println("*** ******** ***");
}
If I run this operation on List<Integer> for example, it works as expected (removes first 5 elements), but when I run it on a list of my objects, nothing happens (list stays the same).
list.subList(0, 5).clear();
My class is a pojo that doesn't implement equals or hashCode, if that matters.
UPDATE:
The implementation I am using is ArrayList, which is returned from Hibernate query. There is nothing to show, really. Sublist doesn't return an empty list.
Here is an example for those who don't beleive it works on a list of Integers:
List<Integer> testList = new ArrayList<Integer>();
for(int i=0;i<10;i++) {
testList.add(i);
}
testList.subList(0, 5).clear();
for(int i=0;i<testList.size();i++) {
System.out.print(testList.get(i)+" ");
}
The result is 5 6 7 8 9
UPDATE2: Actually everything is working as expected, don't know how I couldn't see that (got confused by numbers of results). Sorry for false alarm :) This question could be deleted.
It works on my machinetm
import java.util.*;
import static java.lang.System.out;
class SubListExample {
public static void main( String [] args ) {
List<RandomObject> testList = new ArrayList<RandomObject>();
for(int i=0;i<10;i++) {
testList.add( new RandomObject() );
}
System.out.println( "Before: " + testList );
testList.subList(0, 5).clear();
System.out.println( "After: "+ testList );
}
}
class RandomObject {
static Random generator = new Random();
int id = generator.nextInt(100);
public String toString(){
return "ro("+id+")";
}
}
Produces:
$ java SubListExample
Before: [ro(68), ro(97), ro(48), ro(45), ro(43), ro(69), ro(45), ro(8), ro(88), ro(40)]
After: [ro(69), ro(45), ro(8), ro(88), ro(40)]
So, the problem is not in ArrayList nor in your objects.
I don't think Hibernate returns a plain old ArrayList ( may be it does )
Try printing
System.out.println( "That hibernate list.class.name = "
+ listReturnedByHibernate.getClass().getName() );
And let us know if it is in fact an ArrayList
edit - Looks like I was wrong, but leaving my original answer here anyway:
Are you sure that it works with a List<Integer>? It shouldn't.
The method subList() returns a separate List. If you remove elements from that list, it shouldn't affect the original list. The API docs for List.subList() say this:
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
Clearing a list is not a non-structural change; only changing the elements in the list are non-structural changes.
This has nothing to do with whether your POJO has equals or hashCode methods or not.
edit - I just tried it out with an ArrayList and it does work (not only with Integer, but also with my own object as a list element).
Two things I can think of are:
list.sublist(0, 5) returns an empty list, therefore .clear() does nothing.
Not sure of the inner workings of the List implementation you're using (ArrayList, LinkedList, etc), but having the equals and hashCode implemented may be important.
I had a simiarl issue with Maps, where HashMap definitely needs the hashCode implementation.
Have you tried creating a List of your objects manually and doing the same thing (without Hibernate)? It seems possible to me that this has to do with Hibernate's lazy loading of data... if you haven't read the data in the returned List, it may not have been loaded yet (since sublists themselves are just views). In that case, it's possible clear would do nothing.
Is it possible that the List returned from Hibernate is not modifiable? i.e. wrapped by Collections.unmodifiableList()
This question already has answers here:
Why does the foreach statement not change the element value?
(6 answers)
Closed 5 years ago.
Ok, so I'm tyring to iterate through an ArrayList and remove a specefic element. However, I am having some trouble using the For-Each like structure. When I run the following code:
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for(String t : arr)
{
t = " some other value "; //hoping this would change the actual array
}
for(String t : arr)
{
System.out.println(t); //however, I still get the same array here
}
My question in, how can I make 't' a pointer to 'arr' so that I am able to change the values in a for-each loop? I know I could loop through the ArrayList using a different structure, but this one looks so clean and readable, it would just be nice to be able to make 't' a pointer.
All comments are appreciated! Even if you say I should just suck it up and use a different construct.
I think the best approach may be to use a for loop.
ArrayList<String> arr = new ArrayList<String>();
for (int i = 0; i < arr.size(); i++) {
String t = arr.get(i);
if (// your condition is met) {
arr.set(i, "your new value");
}
}
The problem is that you're trying to change the loop-scoped reference t to let it point to a new String instance. This ain't going to work. It does not refer the actual entry in the arraylist. You need to change the actual value of the reference. If String was mutable and provided a fictive set() method for that, you could in theory do
for (String t : arr) {
t.set("some other value");
}
or so, but that's not possible as it is immutable. Better get a handle of the entrypoint in the array itself using the normal for loop:
for (int i = 0; i < arr.size(); i++) {
arr.set(i, "some other value");
}
If you insist in using the enhanced for loop, then you need to replace String by StringBuilder, which is mutable:
for (StringBuilder t : arr) {
t.delete(0, t.length()).append("some other value");
}
Remember, Java is pass-by-value, not pass-by-reference.
For-each doesn't give you an index pointer, so you just can't use it to change an immutable value.
Either use a for-loop with an index or use a mutable type (like StringBuffer, not String)
An array of objects (like strings) in Java is a contiguous block containing an ordered series of references. So, when you have an array of 4 strings, what you really have is 4 references stored IN the array, and 4 string objects that are outside of the array but are referenced by its 4 elements.
What the for-each construct in Java does is create a local variable and, for each iteration, copy into that local variable the reference from the array cell that corresponds to that iteration. When you set the loop variable (t = " some other value") you are putting a reference to a new string, "some other value", into the local variable t, not into the array.
The contrasts with some other languages (like Perl) where the loop variable acts like an alias to the array/list element itself.
Your code is re-written by the compiler as something like this:
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for (final Iterator <String> i = arr.iterator(); i.hasNext();) {
String t;
t = i.next();
t = " some other value "; // just changes where t is pointing
}
To do what you want you would have to write the for loop like this:
for (final ListIterator<String> i = arr.iterator(); i.hasNext();) {
final String t;
t = i.next();
i.set("some other value");
}
Iterator does not have the set method, only ListIterator does.
Basically you want to remove the String t from the list arr. Just do a arr.remove(t) and you could be done. But you can't do it while iterating over the same list. You'll get an Exception if you try to modify the list this way.
You have two options:
clone your list, iterate through the clone and remove the 'specific' String from the original list
create a list for delete candidates, add all 'specific' Strings to that list and, after iterating through the original list, iterate through the wastebin and remove everything you've collected here from the original list.
Option 1 is the easist, the clone can be made like:
List<String> clone = new ArrayList<String>(arr);
You seem to misunderstand how objects/references work in Java, which is pretty fundamental to using the language effectively. However, this code here should do what you want (apologies for the lack of explanation):
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for(int i = 0; i < arr.size(); i++)
{
arr.set(i, " some other value "); // change the contents of the array
}
for(String t : arr)
{
System.out.println(t);
}
I believe, this is not related to immutable or mutable.
t = " some other value "; //hoping this would change the actual array
t does not hold the reference to actual object. Java copies the value from arraylist and puts that value into t so array list value does not get affect.
HTH
This has been answered well. Still here is my suggestion. The var t inside loop is only visible there. It will not be seen outside the loop. You could do t.set() if it was not String.
Use a StringBuffer rather than plain strings. This way the string within is mutable.
Strings are immutable. If you had a mutable type like StringBuilder/Buffer, you could change the string in your iteration. You do have references, remember.