views:

516

answers:

10

I was making an array of objects, and wanted to run the constructor for the object on every element of the array to initialize them like so:

cells = new Cell[cols];
Arrays.fill(cells, new Cell());

But I started to doubt that it did what I wanted and sure enough Arrays.fill(Object[] o, Object val) doesn't do what I want it to. Essentially fill will run the object constructor once and point every element of the array to that object.

Testing this on StringsBuilders:

StringBuilder[] a = new StringBuilder[10];
Arrays.fill(a,new StringBuilder(""));
a[0].append("1");
System.out.println(Arrays.toString(a));

Confirmed my doubts and printed [1, 1, 1, 1, 1, 1, 1, 1, 1, 1].

I've always used this method with Strings and primitives and never run in to this problem because obviously it's ideal for primitives but it never caused a problem for me with strings because "modifying" them creates a completely new object. (Yes I know, I should be using StringBuilders to do that :P)

So I'm curious as to what are some other things in Java that are commonly misunderstood but people don't notice for a long time?

And just out of curiosity is there another similar Java method that fills an array with unique objects?

+1  A: 

I wouldn't call it "commonly misunderstood" that Arrays.fill() doesn't work in a way other then the documented way. The javadoc clearly states:

Assigns the specified Object reference to each element of the specified array of Objects.

Which means that what ever you send in as a second argument is what it will use as a value for each element. I guess you must be very novice to java if you expects it to call "new Cell()" for every cell and not use the object returned from the new.

Edit: Having said that, there are probably a LOT of things people assumes rather then reading the javadoc or the source to figure out how it works. I once encountered code where the coder called Socket.getInputStream() for every call to it instead of using the same input stream over a series of calls. It would probably work better with todays GCs but it killed performance back then because it resulted in hundreds of InputStreams per second being created.

Fredrik
If we had lambdas...
kd304
@kd304: It would be rather handy to have a method similar to Arrays.fills() which executes a Runnable (or something) for every cell but it is probably not worth it as anyone can code it and get more flexibility.
Fredrik
Have you tried ParallelArray from JSR166y?
kd304
@kd304: I've tried quite a few similar implementations (and use one or two) but not the Java7 implementation.
Fredrik
+7  A: 

what are some other things in Java that are commonly misunderstood but people don't notice for a long time?

Text encodings. Embarassing but true: many (if not most) programmers think a byte and a character (and thus a String and a byte[]) are basically the same thing and can be converted trivially into each other. Unfortunately, Java supports that viewpoint through its implicit usage of a platform default encoding in many places, and unfortunately, you can get by a long time ignoring the issue. Until one day your program has to run on a different computer with a different platform default encoding.

And just out of curiosity is there another similar java method that fills an array with unique objects?

It would be hard to write generically, since it would have to invoke a constructor - then how would it handle classes that have no no-argument constructor?

Michael Borgwardt
With an inner factory class, why not?
kd304
+1 for the encodings... I am so glad I live in the 8859-1 world and isn't hurt by it very often.
Fredrik
Yes, hope the rest of the world's population feels the same.
kd304
@kd304: Being a Swede, it hurts enough when people assume input to only be a-z/0-9 in forms. I am sure the ignorance you bring up hurts a lot in the rest of the world. When I was working with customers in APAC, a few of them had something installed that could be used to do multibyte input in ASCII-based products in a way that shortened the field but actually did the job (if you had the right font). Not very optimal and didn't work well with database queries but an interesting approach to a way to common problem.
Fredrik
Look up "Turkey test"!
starblue
+2  A: 

One thing that is commonly abused is calling a Thread object's run() method, instead of start(). When they call the run method explicitly, it will execute in the caller's thread of control, instead of spawning a new thread.

Using += for String concatenation is often abused too. Most programmers don't realize how expensive it is. If you use += for String inside of a loop, you have serious memory thrashing. When programmers forget that Strings are immutable, lots of memory/efficiency errors arise.

This website has some great commonly misunderstood concepts and API abuses. http://www.javapractices.com/home/HomeAction.do

andystacy
That can be inefficient, but not a 'serious memory leak'. GC should take care of all the created strings.
David Rodríguez - dribeas
By the way, one thing that most people don't understand is how EFFICIENT string concatenation can be in small doses. Creating a string like this: String s="abc"+i+"def"+j..... is actually pretty damn smart, it's only when put into a loop that Java can no longer optimize the strings properly.
Bill K
You're absolutely right. Creating a StringBuffer or StringBuilder for a simple, one-time String concatenation would be quite unnecessary.
andystacy
@andystacy Actually the compiler creates a StringBuilder for each string concatenation, so you don't have to.
starblue
+4  A: 

You definitely can't do this!

for(Cell c: cells) c = new Cell();

That will result in creating ten new cells and losing every reference, because it has the same semantic meaning as this code:

for (int i = 0; i < cells.length; i++) {
    Cell c = cells[i];
    c = new Cell();
}

You are iterating over the values, not the references. Java does not work with references, ever. It works with values of primitives, and values of pointers-to-objects.

The piece of code that does what you originally wanted is this:

for (int i = 0; i < cells.length; i++) {
    cells[i] = new Cell();
}

It isn't elegant, but Java doesn't have a way to do this (that I know of) aside from iterating over the array in some fashion. Even if you create a factory method, it will still have to iterate over the array to fill it.

EDIT: An answer to the more direct part of the original question:

The "synchronized" keyword. I've used it occasionally over the years, but it was always this mysterious thing that kept code thread-safe. (It didn't help that I almost never write multithreaded code, even on a dual-core machine.) Only when I grabbed Concurrent Programming in Java a few weeks ago and read the first couple of chapters did I understand that "synchronized" was nothing more than a way to access the grafted-on lock object that every Object has embedded in itself.

jprete
At first I thought synchronized was some sort of magic that automagically locked. I think the Java tutorial could be clearer on this also.
Maestro1024
+3  A: 

One for sure is the relation between "equals" and "hashCode".

rodrigoap
Or == and equals.
starblue
This answer would be a lot more valuable if you cared to elaborate a bit.. =)
Tim
+7  A: 

Another good reference to use is the book Java Puzzlers. It is similar to the C puzzle book and really gets you to think about the subtleties that can easily trip you up. Plus it's a fun read, with small, short examples so it's easy to pick up for only 10 minutes at a time.

Jill Renee
+1  A: 

Something that trips up a few people around where I work is the java.io.DataInputStream call to skipBytes(int num). The Javadoc does say what it does, but most just assume that skipBytes just skips the number of bytes passed as the parameter. It can do that, but it returns you the number of bytes actually skipped for a reason. It may not actually skip all of the bytes.

And I believe this one is in a Java puzzlers book if its not the Java Puzzlers book.

Jay R.
Findbugs detects this code flaw.
kd304
I meant to add that.Findbugs detects a pile of the Java puzzlers issues.
Jay R.
+4  A: 

Misunderstood: The true potential of the Java language and the JDK library functions.

In general:

interface ObjFactory<T> {
    T create(int index);
}
public static <T> T[] fill(T[] array, ObjFactory<T> f) {
   for (int i = 0; i < array.length; i++) {
       array[i] = f.create(i);
   }
   return array;
}
{
    fill(new String[10], new ObjFactory<String>() { 
        public String create(int index) {
            return String.valueOf(index);
        }
    }
}
kd304
+16  A: 

The most common misconceptions I see are the typical negatives:

  1. Java is slow

    No, Java is wicked fast. For straight code it's typically faster than C until you crank C up to -O3. -O4 will still blow Java away (and yet where at -O4 C has stripped out all its meta info and debugging ability, Java retains it all!).

    Java and C are also about 100x faster than the interpreted languages like Ruby.

  2. Java GUIs are problematic

    Java is so simple to use that people can just jump in and start working, so many don't learn about things like the AWT thread (another good answer for this thread question!). People coding without understanding how the AWT thread works is what makes Java's GUIs clunky.

  3. String concatenation is slow

    Only in loops. As with most stuff in Java, it has actually been optimized to do tricks you wouldn't even have imagined until looking at the source code.

  4. Garbage collection is slow

    GC is actually much faster than C's heap allocation. Most of the time, freeing individual heap objects in Java takes absolutely no processor time. They have a multi-stage system in there that is pure art - top of the industry quality in design and thought. Also it will do "concurrent" GC if enabled that pretty much eliminates freezes during GC (I can't remember the last time I saw as much as a hiccup in Eclipse or NetBeans).

    Note that I'm admittedly cheating here a little. Where C wins this game is in the fact that it generally uses the stack instead of heap allocation which is pretty much the fastest allocation you can run into (except for register allocation, which C will do on the highest optimization levels). Stack allocation doesn't work very well with an OO language though.

  5. Java isn't flexible/expressive and so the code is hard to understand

    This is heavily programmer dependent. I've rarely been forced to add redundancies (copy and paste) in Java code and have cut significant redundancies out of code where the original programmer thought he was being limited by Java.

    If you find Java not expressive enough, you might want to try harder. Look at what EasyMock can do without losing static type safety! After that check into Hibernate and look at annotations. Simply understanding and correctly using OO can do wonders for maintainability.

Bill K
how can it be faster than C? maybe you'll provide a link or whatever?
valya
@valya Google "programming language shootout". The reason is that C cannot do any runtime optimizations (all C's optimizations are static, and without the -O3 I mentioned it doesn't even do many optimizations) whereas Java can compile to machine language at runtime with optimizations built on profiling knowledge. Heap allocations are generally quicker because Java doesn't deallocate individual memory blocks (Google Java and Eden for a good description of one heap allocation algorithm)
Bill K
@Bill. Oh, absolutely. The problem isn't the language, but what people write with it. I am a Java newbie, and even I have found "experienced" Java programmer write ridiculous code. Java makes it easy to write atrocious code that works, and even easier to write APIs, that, in any other language, would get their writers shot. A well written java API/program is a joy. A poorly written one will make you want to rip your eyeballs out.
brice
@Brice True, but what language isn't like that? I'd rather work on a poor Java program than a poor program written in Ruby, Python, any dynamic language or any loosely typed language. The more constraints the better.
Bill K
@Bill I am of a different opinion. I feel that a lot of the time, the java code I come across has been written with the explicit aim of frustrating me, of being buggy, and having unexpected behaviour. Most of the good java code I have come across has been written by people with extensive experience in other languages. Don't get me wrong, I actually like the Java _language_.
brice
@Bill It's just that whenever I get handed a piece of code from a Java-only developer, I get ready for the headaches - which is definitely not the case with most other languages. (Although of course, there are exceptions either way)
brice
@brice I actually agree--pretty much completely. The problem is that, like Basic java is everywhere and (relatively) easy to use. You don't get as much crappy code in C because it's beyond a truly crappy coder's ability to create anything more complicated than hello world. I don't really like the "Let's make it so hard to use it that bad programmers can't use it" approach, but I don't have a solution.. I wonder how the average Ruby code will be perceived in a decade or so.
Bill K
+2  A: 

classpath seem to be a problem for not just new Java coders, but for many intermediate (and probably overly tool-dependent) programmers as well.

Stu Thompson