Showing posts with label for. Show all posts
Showing posts with label for. Show all posts

Saturday, October 10, 2009

refactoring my code (with a little help from my friend Eclipse)

In this post, I'll explain few refactoring tricks and we will use the help of Eclipse as much as possible. Here we go!



Map<String, List<String>> personalItemsMap = new HashMapMap<String, List<String>>();

// for darth vader

List<string> vaderList = new ArrayList<string>();
vaderList.add("lightsaber");
vaderList.add("helmet");
vaderList.add("armor");
personalItemsMap.put("darth vader", vaderList);

// for han solo
List<string> soloList = new ArrayList<string>();
soloList.add("blaster");
soloList.add("vest");
personalItemsMap.put("han solo", soloList);

// for boba fett
List<string> bobaList = new ArrayList<string>();
bobaList.add("blaster");
bobaList.add("mandalorian armor");
personalItemsMap.put("boba fett", bobaList);

System.out.println(personalItemsMap);







Above we create a list of items for each character and add them to an owner-item map. This piece of code is begging for refactoring.



First, I choose the piece of code I'd like to extract as a method.



Then I choose refactor>extract method.


I enter "generateVaderList" as method name and here's our refactored code.


public static void main(String[] args) {
Map<String, List<String>> personalItemsMap = new HashMap()<String, List<String>>;

// for darth vader
List<string> vaderList = createVaderList();
personalItemsMap.put("darth vader", vaderList);

// for han solo
List<string> soloList = new ArrayList<string>();
soloList.add("blaster");
soloList.add("vest");
personalItemsMap.put("han solo", soloList);

// for boba fett
List<string> bobaList = new ArrayList<string>();
bobaList.add("blaster");
bobaList.add("mandalorian armor");
personalItemsMap.put("boba fett", bobaList);

System.out.println(personalItemsMap);
}

private static List createVaderList() {
List vaderList = new ArrayList();
vaderList.add("lightsaber");
vaderList.add("helmet");
vaderList.add("armor");
return vaderList;
}


No need for a temporary reference assignment so I can edit the below part;


List<string> vaderList = createVaderList();
personalItemsMap.put("darth vader", vaderList);


to


personalItemsMap.put("darth vader", createVaderList());


If I do this refactoring for every character my code will be clearer. But think about it. If I do that I'll have three different methods with similar functionality. These methods create a list, add necessary elements to the list and return the list. What if I take these methods and write a more generic one instead of three methods with similar functionality? Later, if I'd like to add a new character I won't write a createNewCharacterList method. I'll use the generic one instead.

Lets see how we can make the method more generic. It creates a list and adds hard-coded strings to it. This method will be general if I hand it the item strings to add. First I choose the name of "createVaderList" and then right click. Then refactor>rename. I rename it as "createItemList". I'm writing a more general method, remember? I can change the method signature from the menu but this is not so handy so I'll skip that and edit my method by hand.


private static List createItemList() {
List<string> vaderList = new ArrayList<string>();
vaderList.add("lightsaber");
vaderList.add("helmet");
vaderList.add("armor");
return vaderList;
}


will become


private static List<string> createItemList(String... items) {
List<string> itemList = new ArrayList<string>();

for(String item : items)
itemList.add(item);

return itemList;
}


In this new method, we -again- create a list. Then we take an arbitrary number of strings. I assure this behavior by using var-args. Then in our beautiful for-loop we add the items to the item list and return the list.

After that we will do the necessary changes in the code. Eclipse can't help us in this case. After we do the necessary changes in our code, our main method will be such as below;


public static void main(String[] args) {
Map<String, List<String>> personalItemsMap = new HashMapMap<String, List<String>>();

// for darth vader
personalItemsMap.put("darth vader", createItemList("lightsaber", "helmet", "armor"));

// for han solo
personalItemsMap.put("han solo", createItemList("blaster", "vest"));

// for boba fett
personalItemsMap.put("boba fett", createItemList("blaster", "mandalorian armor"));

System.out.println(personalItemsMap);
}


Much clearer and more usable right?

Sunday, April 19, 2009

concurrentmodificationexception or: how I learned to stop worrying and loved for-each loop as it is

Few days ago, I got a ConcurrentModificationException out of nowhere while I was iterating through a list and removing elements from it. I remember doing this thing and not getting any exception. I delved a little bit and realized that the exception is thrown because of my misuse of for-each loop (introduced in jdk 5). Its developers noted:
Consider, for example, the expurgate method. The program needs access to the iterator in order to remove the current element. The for-each loop hides the iterator, so you cannot call remove. Therefore, the for-each loop is not usable for filtering. Similarly it is not usable for loops where you need to replace elements in a list or array as you traverse it. Finally, it is not usable for loops that must iterate over multiple collections in parallel.


For-each loop creates and hides the iterator, so when you try to remove an element using the list itself you'll get ConcurrentModificationException because you have to do the removing and adding through the iterator. So the code below will throw ConcurrentModificationException.

/*
integerList is an ArrayList
with Integer objects as elements
*/

for(Integer integer : integerList){
if(integer.intValue()%2 == 0) // remove even elements
integerList.remove(integer);
}


You can fix your error by not using for-each loop. For instance, you can define your iterator and work with it or you can iterate through the array list and do the add/remove directly from the list.

First one's code:


Iterator iterator = integerList.iterator(); // create the iterator
while(iterator.hasNext()){
Integer integer = iterator.next(); // take the element
if(integer.intValue()%2 == 0)
iterator.remove(); // remove using the iterator
}


Second approach's code:



for(int i=0 ; i < integerList.size(); i++){
Integer integer = integerList.get(i); // take the element
if(integer.intValue() %2 == 0){
// remove from the list
integerList.remove(i);
/*
decrease the index by one, so
that we won't skip any elements
after the element left-shifting which
occurs after each removal.
*/

i--;
}
}

Sunday, November 16, 2008

cooler for-loops

JDK 5 offers an interesting enhancement for for-loops. With the new feature, for-loops are less error-prone and with a higher readability. You can convert your for-loop from
for(int i=0; i < integerArray.length; i++) {
System.out.println( integerArray[i] );
}

to
// read below as int i "in" integerArray
for(int i : integerArray){
System.out.println( i );
}

Better, huh? Same stuff works with object arrays too.