Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

February 17, 2011

Doing smart stuff with List and Iterator

Many times the objects in a list must be converted into some others objects, i.e. when
we need to get a different list with converted objects. It's trivial from first glance:
List toJmsMessageList(List originalMyMessageList) {
 List resultList = new ArrayList(originalMyMessageList.size());
 for(MyMessage msg : originalMyMessageList) {
  resultList.add(Converter.convert(msg));
 }
 return resultList;
}
Nothing wrong with the above code, except:
1. it's boring and verbose
2. it creates new list
3. it iterates over original list
4. problems might occur when original list is ORM proxy list obtained as a result
of sql query, so operations such as size() on it - would cause problems such as retrieval of whole result set' content into memory

So, here's solution. What I actually want - convert list' stuff into list of another stuff, I would like to have something like:
List resultList = ListFactory.createList(originalList, callback);
So whenever resultList iteration being called I want to have already converted stuff in it! So, here's what I came to:
 List createList(final Iterator originalIterator, final Callable c) {
 // iteration handler for custom list implementation
 InvocationHandler invocationHandler = new InvocationHandler() {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) {
   if (method.getName().equals("iterator")) {
    return new Iterator() {
     @Override
     public boolean hasNext() {
      return originalIterator.hasNext();
     }

     @Override
     public T next() {
      try {
       // call 'custom behavior'
       return c.call();
      } catch (Exception e) {
       log.error("Error during user defined operation on originalIterator", e);
       throw new RuntimeException(e);
      }
     }

     @Override
     public void remove() {
      // 'remove' isn't supported
      throw new UnsupportedOperationException();
     }
    };
   }
   // everthying other than iteration isn't supported
   throw new UnsupportedOperationException();
  }
 };
 return (List) Proxy.newProxyInstance(
     Thread.currentThread().getClass().getClassLoader(),
     new Class[] {List.class}, 
     invocationHandler);
}
Here's possible use cases:
return ListFactory.createList(originalListIterator, new Callable() {
 private int fetchedNum = 0; // the number of fetched objects
 @Override
 public MessageEntity call() {
  if (++fetchedNum % fetchSize == 0) {
   // remove fetched MessageEntity entities from iternal jpa's identity map
   // when number of fetched objects reaches 'fetchedNum'
   getEntityManager().clear();
  }
  // don't forget call iterator.next()
  return originalListIterator.next();
 }
});
or
ListFactory.createList(messagesIterator, new Callable() {
 @Override
 public Entry call() {
  try {
   return toEntry(messagesIterator.next());
  } catch (Exception e) {
   log.error("Error occured while converting list of jms messages", e);
   throw new RuntimeException(e);
  }
 }
});

Thanks for reading.