algorithm-archivists / algorithm-archive

A collaborative book on algorithms
https://www.algorithm-archive.org
MIT License
2.33k stars 353 forks source link

Java #972

Closed Amaras closed 2 years ago

Amaras commented 2 years ago

This is an issue to ask how to address the compilation of Java code. Do we want to have classes around, or do we want a .jar file?

PeanutbutterWarrior commented 2 years ago

I think that requiring a manifest file is pointless, so we should compile to .class files. I don't like the idea of just copying the java files, as it's usually considered compiled and we may as well test building.

Amaras commented 2 years ago

Fair. I'll need to alter the compilation target slightly to only include the chapter name, and fix the problems with the classes. It might be a problem, though

WWelna commented 2 years ago

I checked a few of the examples in Java and they seem to be very straight forward to compile and run, using only the standard imports.

From: https://github.com/algorithm-archivists/algorithm-archive/blob/93d4a178e279169a0c79feeb2f2166a986b66237/contents/tree_traversal/code/java/Tree.java

wwelna@COINTEL-WORKSTATION:~$ javac Tree.java 
wwelna@COINTEL-WORKSTATION:~$ java Tree
[#]
Recursive DFS:
2 1 0 0 0 1 0 0 0 1 0 0 0 
[#]
Recursive Postorder DFS:
0 0 0 1 0 0 0 1 0 0 0 1 2 
[#]
Stack-based DFS:
2 1 0 0 0 1 0 0 0 1 0 0 0 
[#]
Queue-based BFS:
2 1 1 1 0 0 0 0 0 0 0 0 0 
[#]
Recursive Inorder DFS for Binary Tree:
0 1 0 2 0 1 0 3 0 1 0 2 0 1 0 
wwelna@COINTEL-WORKSTATION:~$ ls -lah Tree.*
-rw-rw-r-- 1 wwelna wwelna 3.4K Dec  8 02:03 Tree.class
-rw-rw-r-- 1 wwelna wwelna 4.5K Dec  8 02:03 Tree.java
wwelna@COINTEL-WORKSTATION:~$ 

The only problem would be if the examples use newer features or have conflicts with older versions of Java using a severely outdated version of jdk. This should not be a problem as long as the examples stick to the basic core functionality of Java.

I'd just keep doing it in this format, as doing builds for jars adds a lot of unneeded complexity for what amounts to executing a single class inside a jar VS the single javac command to compile, and the second java command to run the example. This also keeps the 'build and run the example instructions' short and simple, with little room for errors or issues with the build environment, with the only requirement is having a jdk installed.

Edit: As far as I am aware, Java has always had the compile and run a class file. This is oldest documentation of it I could find at a quick glance, from Java 6.

Amaras commented 2 years ago

Thank you for your answer, it is very appreciated.

However, the main problem I have is with the stacks and queues chapter, where we have two non-obvious entry points...

WWelna commented 2 years ago

I apologize beforehand but this is going to bit a bit of a long reply...

There is a few problems with those two examples. With Java, the name of the file must be the public class declared in said file, so "Queue.java" needs to be renamed to "QueueTest.java", and "Stack.java" needs to be renamed to "StackTest.java" (or rename the "QueueTest" class to "Queue", etc). You also can not have two public declared classes in the same .java file, but you can declare and use additional classes in a single .java file, but they are only usable within the scope of that declared public class, and can have no scope modifiers. Just remove the "public" modifier on lines 46 & 45 in each respective example code, and it will work.

Entry point is always public static void main(String[] args) function of the public declared class, unless it's a jar (collection of compiled classes in a zip file with a bit of meta data added), which you can have multiple public classes as an entry point, you can specify on the command line which one you want to execute, but usually a defined one is in the manifest.

Basically, this is part of Java's builtin forced source code lint, which does have advantages, making it easier to find where a specific piece of code is, with strict rules on how you structure code.

Now, as for the implementation, it's not what I'd expect, as ArrayList is a just another Collection. The Collection interface and it's classes implementing them are all coded in Java using primitive types. As an example, we can look at how ArrayList works. So... Java doesn't have dynamic arrays, you can only declare fixed sized arrays of objects (or primitives), so the ArrayList class is basically a wrapper around a fixed array that expands via creating a new array of current size + preallocated amount, and copying references from the previous objects to the new array, keeping track of the entries, and expanding when needed. I think this would be a more ideal way of how to show how this works in Java rather than using one Collection to emulate another Collection. To note, C# is also like this with the lack of dynamic arrays, so the solution would be similar.

Amaras commented 2 years ago

There is a few problems with those two examples. With Java, the name of the file must be the public class declared in said file, so "Queue.java" needs to be renamed to "QueueTest.java", and "Stack.java" needs to be renamed to "StackTest.java" (or rename the "QueueTest" class to "Queue", etc). You also can not have two public declared classes in the same .java file, but you can declare and use additional classes in a single .java file, but they are only usable within the scope of that declared public class, and can have no scope modifiers. Just remove the "public" modifier on lines 46 & 45 in each respective example code, and it will work.

This is indeed what I did in #973.

Now, as for the implementation, [...]

Fair enough, however you can open a PR to change the implementation if you wish, as I am not really interested in making them good Java code, just making them compile correctly (and we will also need to make them run correctly, but that is not what we're trying to do currently). Output validation is what we need to do for #824. Making the code good is something we should have done during the review process, but very few of us are Java experts, so if you have some time on your hands, please review the Java PRs and edit the existing implementations.