visit
The Java Collections Framework was a huge leap forward when it was introduced as part of Java 2 (JDK 1.2). Thanks to the included collection classes we finally moved beyond the limits of Vector
and Hashtable
to more mature and generic solutions. With the introduction of streams and functional concepts in Java 8 the framework took everything to the next level.
With local debugging we can just add an inspection such aslist.toArray()
. This will perform poorly but will still work. However, in a production environment when using this will fail. When trying to print out a complex list we can fail on the method invocation itself (which might fall below quota) or simply the length of the output which might be cropped.
Printing the content of a collection of elements is problematic. Even if you have code that uses the Iterable
interface to loop over the entire list the likelihood of avoiding quota restrictions is low. Printing a primitive type array is easy but printing objects requires more.
List<MyObject> myList = new ArrayList<>();
Then the log might look like this:
The property value of the first element is {myList.get(0).getProperty()}
This will fail.
Generics in Java are removed during compilation and have no effect in the bytecode. As such, Lightrun which works at the bytecode level is oblivious to them. The solution is to write the code as if the generic isn’t present and cast to the appropriate class:
The property value of the first element is {((MyObject)myList.get(0)).getProperty()}
Debugging is the process of making assumptions and validating them. The size()
method from java collections is very efficient and can be used almost freely. If you expect a result to include a fixed set of elements you can easily use the size()
or isEmpty()
methods to indicate if a collection fits expectations. The method invocation here will be very efficient.
vet.getFirstName().equals("Shai")
If it’s met I can print out the full details for the entry: Current vet is {newVet}
.
return vets.findAllByOrderById(Pageable.ofSize(5).withPage(page)).stream().map(vet -> {
VetDTO newVet = new VetDTO();
newVet.setId(vet.getId());
newVet.setLastName(vet.getLastName());
newVet.setFirstName(vet.getFirstName());
Set<PetDTO> pets = findPetDTOSet(vet.getId());
newVet.setPets(pets);
return newVet;
}).collect(Collectors.toList());
It seems so much cooler than this code which returns from the method after value assignment:
List<VetDTO> returnValue = vets.findAllByOrderById(Pageable.ofSize(5).withPage(page)).stream().map(vet -> {
VetDTO newVet = new VetDTO();
newVet.setId(vet.getId());
newVet.setLastName(vet.getLastName());
newVet.setFirstName(vet.getFirstName());
Set<PetDTO> pets = findPetDTOSet(vet.getId());
newVet.setPets(pets);
return newVet;
}).collect(Collectors.toList());
return returnValue;
But the second one lets us debug the collection locally as well as remotely. It also makes it much easier to add a log statement covering the collection result value which is something you should generally consider.
This is especially true when dealing with Java streams which emphasize such terse syntax.
I cannot stress this enough: if it goes into the collection framework it should have a toString()
method in the class. This makes debugging the elements so much easier!
When we include the class in a snapshot or a log the toString()
method is invoked. If there’s no implementation in the class we will see the object ID which isn’t as useful.
Printing everything in an Iterable
interface won’t work but using a conditional statement to print only the line that matters can work rather well.
Standard methods in the collection might still be too expensive for the quota CPU time mechanism. But APIs such as isEmpty()
or ``size()`are efficient.
Also Published