Update:
Some 10 years later perhaps the best way to test a private method, or any inaccessible member, is via @Jailbreak
from the Manifold framework.
@Jailbreak Foo foo = new Foo();
// Direct, *type-safe* access to *all* foo's members
foo.privateMethod(x, y, z);
foo.privateField = value;
This way your code remains type-safe and readable. No design compromises, no overexposing methods and fields for the sake of tests.
If you have somewhat of a legacy Java application, and you're not allowed to change the visibility of your methods, the best way to test private methods is to use reflection.
Internally we're using helpers to get/set private
and private static
variables as well as invoke private
and private static
methods. The following patterns will let you do pretty much anything related to the private methods and fields. Of course, you can't change private static final
variables through reflection.
Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);
And for fields:
Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
Notes:
1. TargetClass.getDeclaredMethod(methodName, argClasses)
lets you look into private
methods. The same thing applies for
getDeclaredField
.
2. The setAccessible(true)
is required to play around with privates.
Summary ArrayList
with ArrayDeque
are preferable in many more use-cases than LinkedList
. If you're not sure — just start with ArrayList
.
TLDR, in ArrayList accessing an element takes constant time [O(1)] and adding an element takes O(n) time [worst case]. In LinkedList adding an element takes O(n) time and accessing also takes O(n) time but LinkedList uses more memory than ArrayList.
LinkedList
and ArrayList
are two different implementations of the List interface. LinkedList
implements it with a doubly-linked list. ArrayList
implements it with a dynamically re-sizing array.
As with standard linked list and array operations, the various methods will have different algorithmic runtimes.
For LinkedList<E>
get(int index)
is O(n) (with n/4 steps on average), but O(1) when index = 0
or index = list.size() - 1
(in this case, you can also use getFirst()
and getLast()
). One of the main benefits of LinkedList<E>
add(int index, E element)
is O(n) (with n/4 steps on average), but O(1) when index = 0
or index = list.size() - 1
(in this case, you can also use addFirst()
and addLast()
/add()
). One of the main benefits of LinkedList<E>
remove(int index)
is O(n) (with n/4 steps on average), but O(1) when index = 0
or index = list.size() - 1
(in this case, you can also use removeFirst()
and removeLast()
). One of the main benefits of LinkedList<E>
Iterator.remove()
is O(1). One of the main benefits of LinkedList<E>
ListIterator.add(E element)
is O(1). One of the main benefits of LinkedList<E>
Note: Many of the operations need n/4 steps on average, constant number of steps in the best case (e.g. index = 0), and n/2 steps in worst case (middle of list)
For ArrayList<E>
get(int index)
is O(1). Main benefit of ArrayList<E>
add(E element)
is O(1) amortized, but O(n) worst-case since the array must be resized and copied
add(int index, E element)
is O(n) (with n/2 steps on average)
remove(int index)
is O(n) (with n/2 steps on average)
Iterator.remove()
is O(n) (with n/2 steps on average)
ListIterator.add(E element)
is O(n) (with n/2 steps on average)
Note: Many of the operations need n/2 steps on average, constant number of steps in the best case (end of list), n steps in the worst case (start of list)
LinkedList<E>
allows for constant-time insertions or removals using iterators, but only sequential access of elements. In other words, you can walk the list forwards or backwards, but finding a position in the list takes time proportional to the size of the list. Javadoc says "operations that index into the list will traverse the list from the beginning or the end, whichever is closer", so those methods are O(n) (n/4 steps) on average, though O(1) for index = 0
.
ArrayList<E>
, on the other hand, allow fast random read access, so you can grab any element in constant time. But adding or removing from anywhere but the end requires shifting all the latter elements over, either to make an opening or fill the gap. Also, if you add more elements than the capacity of the underlying array, a new array (1.5 times the size) is allocated, and the old array is copied to the new one, so adding to an ArrayList
is O(n) in the worst case but constant on average.
So depending on the operations you intend to do, you should choose the implementations accordingly. Iterating over either kind of List is practically equally cheap. (Iterating over an ArrayList
is technically faster, but unless you're doing something really performance-sensitive, you shouldn't worry about this -- they're both constants.)
The main benefits of using a LinkedList
arise when you re-use existing iterators to insert and remove elements. These operations can then be done in O(1) by changing the list locally only. In an array list, the remainder of the array needs to be moved (i.e. copied). On the other side, seeking in a LinkedList
means following the links in O(n) (n/2 steps) for worst case, whereas in an ArrayList
the desired position can be computed mathematically and accessed in O(1).
Another benefit of using a LinkedList
arises when you add or remove from the head of the list, since those operations are O(1), while they are O(n) for ArrayList
. Note that ArrayDeque
may be a good alternative to LinkedList
for adding and removing from the head, but it is not a List
.
Also, if you have large lists, keep in mind that memory usage is also different. Each element of a LinkedList
has more overhead since pointers to the next and previous elements are also stored. ArrayLists
don't have this overhead. However, ArrayLists
take up as much memory as is allocated for the capacity, regardless of whether elements have actually been added.
The default initial capacity of an ArrayList
is pretty small (10 from Java 1.4 - 1.8). But since the underlying implementation is an array, the array must be resized if you add a lot of elements. To avoid the high cost of resizing when you know you're going to add a lot of elements, construct the ArrayList
with a higher initial capacity.
If the data structures perspective is used to understand the two structures, a LinkedList is basically a sequential data structure which contains a head Node. The Node is a wrapper for two components : a value of type T [accepted through generics] and another reference to the Node linked to it. So, we can assert it is a recursive data structure (a Node contains another Node which has another Node and so on...). Addition of elements takes linear time in LinkedList as stated above.
An ArrayList, is a growable array. It is just like a regular array. Under the hood, when an element is added at index i, it creates another array with a size which is 1 greater than previous size (So in general, when n elements are to be added to an ArrayList, a new array of size previous size plus n is created). The elements are then copied from previous array to new one and the elements that are to be added are also placed at the specified indices.
Best Answer
What you are asking is a pretty tough question. While you may think it is just one question, you are actually asking several questions at once. I'll do my best with the knowledge that I have to cover it and, hopefully, some others will join in to cover what I may miss.
Nested Classes: Introduction
As I'm not sure how comfortable you are with OOP in Java, this will hit a couple of basics. A nested class is when a class definition is contained within another class. There are basically two types: Static Nested Classes and Inner Classes. The real difference between these are:
Garbage Collection and Inner Classes
Garbage Collection is automatic but tries to remove objects based on whether it thinks they are being used. The Garbage Collector is pretty smart, but not flawless. It can only determine if something is being used by whether or not there is an active reference to the object.
The real issue here is when a inner class has been kept alive longer than its container. This is because of the implicit reference to the containing class. The only way this can occur is if an object outside of the containing class keeps a reference to the inner object, without regard to the containing object.
This can lead to a situation where the inner object is alive (via reference) but the references to the containing object has already been removed from all other objects. The inner object is, therefore, keeping the containing object alive because it will always have a reference to it. The problem with this is that unless it is programmed, there is no way to get back to the containing object to check if it is even alive.
The most important aspect to this realization is that it makes no difference whether it is in an Activity or is a drawable. You will always have to be methodical when using inner classes and make sure that they never outlive objects of the container. Luckily, if it isn't a core object of your code, the leaks may be small in comparison. Unfortunately, these are some of the hardest leaks to find, because they are likely to go unnoticed until many of them have leaked.
Solutions: Inner Classes
Activities and Views: Introduction
Activities contain a lot of information to be able to run and display. Activities are defined by the characteristic that they must have a View. They also have certain automatic handlers. Whether you specify it or not, the Activity has an implicit reference to the View it contains.
In order for a View to be created, it must know where to create it and whether it has any children so that it can display. This means that every View has an reference to the Activity (via
getContext()
). Moreover, every View keeps references to its children (i.e.getChildAt()
). Finally, each View keeps a reference to the rendered Bitmap that represents its display.Whenever you have a reference to an Activity (or Activity Context), this means that you can follow the ENTIRE chain down the layout hierarchy. This is why memory leaks regarding Activities or Views are such a huge deal. It can be a ton of memory being leaked all at once.
Activities, Views and Inner Classes
Given the information above about Inner Classes, these are the most common memory leaks, but also the most commonly avoided. While it is desirable to have an inner class have direct access to an Activities class members, many are willing to just make them static to avoid potential issues. The problem with Activities and Views goes much deeper than that.
Leaked Activities, Views and Activity Contexts
It all comes down to the Context and the LifeCycle. There are certain events (such as orientation) that will kill an Activity Context. Since so many classes and methods require a Context, developers will sometimes try to save some code by grabbing a reference to a Context and holding onto it. It just so happens that many of the objects we have to create to run our Activity have to exist outside of the Activity LifeCycle in order to allow the Activity to do what it needs to do. If any of your objects happen to have a reference to an Activity, its Context, or any of its Views when it is destroyed, you have just leaked that Activity and its entire View tree.
Solutions: Activities and Views
getBaseContext()
orgetApplicationContext()
). These do not keep references implicitly.Runnables: Introduction
Runnables are actually not that bad. I mean, they could be, but really we've already hit most of the danger zones. A Runnable is an asynchronous operation that performs a task independant from the thread it was created on. Most runnables are instantiated from the UI thread. In essence, using a Runnable is creating another thread, just slightly more managed. If you class a Runnable like a standard class and follow the guidelines above, you should run into few problem. The reality is that many developers do not do this.
Out of ease, readability and logical program flow, many developers utilize Anonymous Inner Classes to define their Runnables, such as the example you create above. This results in an example like the one you typed above. An Anonymous Inner Class is basically a discrete Inner Class. You just don't have to create a whole new definition and simply override the appropriate methods. In all other respects it is a Inner Class, which means that it keeps an implicit reference to its container.
Runnables and Activities/Views
Yay! This section can be short! Due to the fact that Runnables run outside of the current thread, the danger with these comes to long running asynchronous operations. If the runnable is defined in an Activity or View as an Anonymous Inner Class OR nested Inner Class, there are some very serious dangers. This is because, as previously stated, it has to know who its container is. Enter the orientation change (or system kill). Now just refer back to the previous sections to understand what just happened. Yes, your example is quite dangerous.
Solutions: Runnables
Answering the Final Question Now to answer the questions that were not directly addressed by the other sections of this post. You asked "When can an object of an inner class survive longer than its outer class?" Before we get to this, let me reemphasize: though you are right to worry about this in Activities, it can cause a leak anywhere. I shall provide a simple example (without using an Activity) just to demonstrate.
Below is a common example of a basic factory (missing the code).
This is a not as common example, but simple enough to demonstrate. The key here is the constructor...
Now, we have Leaks, but no Factory. Even though we released the Factory, it will remain in memory because every single Leak has a reference to it. It doesn't even matter that the outer class has no data. This happens far more often than one might think. We don't need the creator, just its creations. So we create one temporarily, but use the creations indefinitely.
Imagine what happens when we change the constructor just slightly.
Now, every single one of those new LeakFactories has just been leaked. What do you think of that? Those are two very common examples of how a inner class can outlive an outer class of any type. If that outer class had been an Activity, imagine how much worse it would have been.
Conclusion
These list the primarily known dangers of using these objects inappropriately. In general, this post should have covered most of your questions, but I understand it was a loooong post, so if you need clarification, just let me know. As long as you follow the above practices, you will have very little worry of leakage.