I am starting to collect a long list of programming languages that I have worked with, but in reality, when I need to switch from one to another, it takes me a bit of time to get used to them again, specially after a while.
A couple of months back, I’ve challenged myself to practice a language each month, by solving a daily problem, to keep myself constantly updated on them. Since challenges seem to be fashionable, you can call this the #30DaysOfProgrammingLanguages challenge.
To do this, I started working with leetcode, which happens to have monthly challenges, so the choice of those problems was already conveniently made for me. These problems don’t take long to complete, and if they do it means I need to practice that concept, which in turns means that it’s a good thing to go over them.
I did April’s challenge with C# and May’s with Java, and here is what I’ve learnt:
Before starting my feeling was that both languages are close enough and I was going to need to change very little in between them, maybe some grammar. On the other hand, I thought that one of these coding challenges per month was not going to be enough to review all the details of the languages and to really get them I was going to need to build some app.
Java prefers classes in uppercase, and methods in lowercase, the opposite as C#.
Length, length(), size(), count:
I am still wondering why do we need different grammar for the same concept. As far as I remember:
- For C#, arrays use Length, strings Length and Lists and most other classes under Icollection, Count.
- Similarly, for Java, arrays use length, string length() and the others usually size().
When in doubt, remember the rule of lowercase in Java, upper in C#.
Values and references:
For problems such as Validate Binary Search Tree, sometimes is convenient to send “null” for the first iteration of the methods. In Java, this means passing the object instead of the data type (so you are getting reference instead of value). In C#, it is possible to do this too, using nullable value types (“int?“), but I actually struggled many times with this problem in C# because this was not introduced until C# 8.0 (.NET core 3.0). It is possible to solve it without passing null, it’s just faster and cleaner to use that.
The following are two equivalent function definitions, for C# first, and Java second.
private bool isValid(TreeNode node, int? left, int? right);
private boolean isValid(TreeNode root, Integer lower, Integer higher);
Dictionaries and maps:
This is probably what needs biggest attention. I honestly find dictionaries much easier to work with in C# than in Java. For example, access of an element is done with , in Java you need the get() method, which is already more typing and more confusing when getting inside other statements that use parentheses.
Iterating through a dictionary (in my experience, if you need to do this, keep in mind that there is probably a better way of doing that exercise than the one you are thinking of), is done by KeyValuePair in C# and entrySet() in Java.
On initializing, you need to use objects for Java while you C# would use data types when possible, which means you need to compare with equals rather than ==. Check below’s example for this last point, first code is written in C# and second the Java equivalent.
Dictionary<Char, Int32> myMap = new Dictionary<Char, Int32>(); // Initialization omitted. 'a' and 'b' have the same integer. myMap['a']==myMap['b']; // -->works myMap['a'].equals(myMap['b']); // --> Compiler error: 'int' does not contain a definition for 'equals' and no accessible extension method 'equals' accepting a first argument of type 'int' could be found
HashMap<Character, Integer> myMap = new HashMap<>(); // Initialization omitted. 'a' and 'b' have the same integer. myMap.get('a') == myMap.get('b'); //-> sometimes would return false when true myMap.get('a').equals(myMap.get('b')); //-> works
Note as well how the use of generic are slightly different for both of them. Since most times it might not cause an issue, because the way the heap works, it might cause issues that are hard to reproduce. This was one of the problems that helped me reproduce the issue (in particular when there was a case with a very long string with at least two characters with the same frequency).
Interfaces for the data structures:
Interfaces in C# conventionally start with “I”, while in Java they might have a different names. One more example follows, C# code firstly and Java equivalent after.
IList<T> myList = new List<>();
List<T> myList = new ArrayList<>(); // There is no "List" class
Below is a generic diagram of equivalence of collection classes in Java vs C#, where left and blue represent Java conventions and green and right C#. Note the classes below are for the C# generic collection, but you might find keywords that would match Java’s in C# which are not from the generic collection (you can’t use <T> with them). The use of these are not recommended and I have not added them in the diagram’s C# side.
There are also some concurrent classes that are not included.
Let me know if you want another article comparing class by class and zooming in on each group, or if you find any errata on the diagram.
One great thing about leetcode is that once you have finished the challenge, it shows you a graph comparing your solution’s performance with others. That gives you the opportunity of checking how others did it and improving your skills. By doing this, I realized that I needed to refresh my knowledge about switch statements in c#.
Things I did not get to review
There were also some things I did not happen to find and I wish I would have reviewed:
- Regular expressions
- Lambda expressions
- File manipulation
- Concurrency and async programming
- Iterators over data structures (more in depth)
Another issue to mention is that most times you can get away changing the objects that were passed by parameters in the problems, without penalty from leetcode, and this could lead to problems in a bigger code base.
It will be nice to have challenges that highlight these in a git repository or leetcode collection and add some reviewing these concepts.
You can see other differences here.
I actually completed several of these problems per day, and some of the days I would switch back and forth (especially if I find something interesting), but the point is to keep constantly updated with the languages and I was looking for something that I could do daily without getting burnt out. Even so, I did find many things I did not remember of the languages and I refreshed more than the syntax. I think under daily busy situations, one problem a day is still achievable.
If I was to go back to some versions of say, Java, I would encounter different issues than the aforementioned. Keep in mind these are the ones I noticed at the time of writing this article.
Overall, it was a great experience and review of concepts. I am looking forward to keep refreshing other languages and see how it affects me in the long run. So far, I feel I’m slower when writing code because I keep forgetting the right grammar and particulars of each language, hopefully this will help me getting faster while keeping me up to date across them all. I will keep comparing other languages and updating my experience, but that’s well..another story.