I have a very simple Java bean, WatchedFile
, which has a fileName
field.
I would like to sort a fj.data.List
of WatchedFile
objects, but I'm struggling with defining an fj.Ord
for the list's sort()
method. This is what I came up with:
protected List<WatchedFile> getWatchedFileList(String path) throws IOException {
List<File> files = List.list(new File(path).listFiles());
return files
.map((file) -> new WatchedFile(file.getName(), false, file.length()))
.sort(Ord.ord(new F<WatchedFile, F<WatchedFile, Ordering>>()
{
@Override
public F<WatchedFile, Ordering> f(final WatchedFile watchedFile1)
{
return new F<WatchedFile, Ordering>()
{
@Override
public Ordering f(final WatchedFile watchedFile2)
{
int compareResult = watchedFile1.fileName.compareTo(watchedFile2.fileName);
return (compareResult < 0 ? Ordering.LT :
(compareResult > 0 ? Ordering.GT : Ordering.EQ));
}
};
}
}));
}
This is ugly! I'm sure there is a better way of instantiating an Ord
object... Possibly utilizing some Java 8 magick?
protected List<WatchedFile> getWatchedFileList(String path) throws IOException {
List<File> files = Arrays.asList(new File(path).listFiles());
return files.stream()
.map(file -> new WatchedFile(file.getName(), false, file.length()))
.sorted((wf1, wf2)->wf1.fileName.compareTo(wf2.fileName))
.collect(Collectors.toList());
}
It’s recommended to have a method public String getFileName()
in your class WatchedFile
. In that case you can simply say:
protected List<WatchedFile> getWatchedFileList(String path) throws IOException {
List<File> files = Arrays.asList(new File(path).listFiles());
return files.stream()
.map(file -> new WatchedFile(file.getName(), false, file.length()))
.sorted(Comparator.comparing(WatchedFile::getFileName))
.collect(Collectors.toList());
}
And, using NIO2 for getting the directory entries, it may look like:
protected List<WatchedFile> getWatchedFileList(String path) throws IOException {
try {
return Files.list(Paths.get(path))
.map(p -> new WatchedFile(p.toString(), false, fileSize(p)))
.sorted(Comparator.comparing(WatchedFile::getFileName))
.collect(Collectors.toList());
} catch(UncheckedIOException ex) { throw ex.getCause(); }
}
private long fileSize(Path path) {
try { return Files.size(path); }
catch (IOException ex) { throw new UncheckedIOException(ex); }
}
If you want to stay within the “functional-java” API, a solution can look like:
protected List<WatchedFile> getWatchedFileList(String path) throws IOException {
List<File> files = List.list(new File(path).listFiles());
return files
.map(file -> new WatchedFile(file.getName(), false, file.length()))
.sort(Ord.stringOrd.comap(wf -> wf.fileName));
}
The key point is that you don’t need (shouldn’t) re-implement the way, String
s are compared. Instead, specify the function to get the property value to compare. Compare with Java 8 factory method Comparator.comparing
used in the second code example.