Last active
April 6, 2019 15:03
-
-
Save VenkataRaju/18500aba90bdb62a02fda352986adfc6 to your computer and use it in GitHub Desktop.
Java File Related Statistics
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public final Stats | |
{ | |
public static void topJavaFilesByLoc() throws Exception | |
{ | |
try (ZipFile zipFile = new ZipFile("/usr/lib/jvm/java-11.0.1-openjdk-amd64/lib/src.zip")) | |
{ | |
Map<Long, Set<String>> fileNamesByNumberOfLines = zipFile.stream() | |
.map(zipEntry -> | |
{ | |
if (zipEntry.isDirectory()) | |
return null; | |
var name = zipEntry.getName(); | |
int slashIndex = name.indexOf('/'); | |
if (!name.regionMatches(slashIndex + 1, "java", 0, 4)) | |
return null; | |
var fileNameWithPkg = name.substring(slashIndex + 1, name.length() - 5).replace('/', '.'); | |
try (var br = new BufferedReader(new InputStreamReader(zipFile.getInputStream(zipEntry), StandardCharsets.UTF_8))) | |
{ | |
return Map.entry(br.lines().count(), fileNameWithPkg); | |
} | |
catch (IOException e) | |
{ | |
throw new UncheckedIOException(e); | |
} | |
}) | |
.filter(Objects::nonNull) | |
.collect(Collectors.groupingBy(Entry::getKey, | |
() -> new TreeMap<Long, Set<String>>(Comparator.reverseOrder()), | |
Collectors.mapping(Entry::getValue, Collectors.toCollection(TreeSet::new)))); | |
System.out.println("----------------"); | |
System.out.println(" LOC | Class"); | |
System.out.println("----------------"); | |
fileNamesByNumberOfLines.entrySet().stream() | |
.limit(10) | |
.forEach(e -> System.out.printf("%,6d | %s%n", e.getKey(), String.join(", ", e.getValue()))); | |
/** | |
* <pre> | |
---------------- | |
LOC | Class | |
---------------- | |
10,683 | java.lang.Character | |
10,515 | java.awt.Component | |
9,730 | javax.swing.JTable | |
8,906 | java.util.Arrays | |
6,382 | java.util.concurrent.ConcurrentHashMap | |
5,974 | java.lang.invoke.MethodHandles | |
5,961 | java.util.regex.Pattern | |
5,669 | javax.swing.JTree | |
5,638 | javax.swing.JComponent | |
5,616 | java.util.Collections | |
* </pre> | |
*/ | |
} | |
} | |
static void publicOverloadedMethodStats() | |
{ | |
Map<Long, Set<String>> map = Stream.of(Arrays.class.getDeclaredMethods()) | |
.filter(m -> Modifier.isPublic(m.getModifiers())) | |
.collect(Collectors.groupingBy(Method::getName, Collectors.counting())) | |
.entrySet() | |
.stream() | |
.collect(Collectors.groupingBy( | |
Entry::getValue, | |
TreeMap::new, | |
Collectors.mapping(Entry::getKey, Collectors.toCollection(TreeSet::new)))); | |
System.out.println("Overloaded | No. of Methods"); | |
System.out.println("-----------------------------"); | |
long noOfPublicMethods = 0; | |
for (Entry<Long, Set<String>> e : map.entrySet()) | |
{ | |
long key = e.getKey(); | |
Set<String> val = e.getValue(); | |
noOfPublicMethods += key * val.size(); | |
System.out.printf("%2d %-5s | %2d methods (%s)%n", key, (key == 1 ? "time" : "times"), val.size(), String.join(", ", val)); | |
} | |
System.out.println("Total No. of public methods: " + noOfPublicMethods); | |
// Overloaded | No. of Methods | |
// ----------------------------- | |
// 1 time | 4 methods (asList, deepEquals, deepHashCode, deepToString) | |
// 4 times | 2 methods (parallelSetAll, setAll) | |
// 8 times | 4 methods (compareUnsigned, parallelPrefix, spliterator, stream) | |
// 9 times | 2 methods (hashCode, toString) | |
// 10 times | 2 methods (copyOf, copyOfRange) | |
// 18 times | 4 methods (binarySearch, fill, parallelSort, sort) | |
// 20 times | 3 methods (compare, equals, mismatch) | |
// Total No. of public methods: 214 | |
} | |
static void publicOverloadedMethodStatsWithInPackage() throws Exception | |
{ | |
try (ZipFile zipFile = new ZipFile("/usr/lib/jvm/java-9-oracle/lib/src.zip")) | |
{ | |
Map<Entry<String, String>, Long> noOfMethodOccurancesByClassAndMethodName = zipFile.stream() | |
.map(ZipEntry::getName) | |
.filter(zen -> zen.startsWith("java.base/java/") && !zen.endsWith("package-info.java")) | |
.map(zen -> | |
{ | |
StringBuilder sb = new StringBuilder(zen.length() + 1); | |
for (int i = 10, len = zen.length() - 5; i < len; i++) | |
{ | |
char c = zen.charAt(i); | |
sb.append(c == '/' ? '.' : c); | |
} | |
return sb.toString(); | |
}) | |
.map(className -> | |
{ | |
try | |
{ | |
return Class.forName(className); | |
} | |
catch (ClassNotFoundException e) | |
{ | |
System.out.println("Could not load class: " + className); | |
return null; | |
} | |
}) | |
.filter(Objects::nonNull) | |
.flatMap(c -> Stream.of(c.getDeclaredMethods())) | |
.filter(m -> Modifier.isPublic(m.getModifiers())) | |
.collect(Collectors.groupingBy(m -> Map.entry(m.getDeclaringClass().getTypeName(), m.getName()), Collectors.counting())); | |
// Eclipse fails to compile all the expression at once | |
Comparator<Entry<String, String>> entryComparator = Comparator.comparing(Entry::getKey); | |
Map<Long, Set<Entry<String, String>>> classAndMethodsByNoOfMethodOccurances = noOfMethodOccurancesByClassAndMethodName | |
.entrySet() | |
.stream() | |
.collect(Collectors.groupingBy( | |
Entry::getValue, | |
TreeMap::new, | |
Collectors.mapping(Entry::getKey, | |
Collectors.toCollection(() -> new TreeSet<>(entryComparator.thenComparing(Entry::getValue)))))); | |
System.out.println("Overloaded | No. of Methods"); | |
System.out.println("-----------------------------"); | |
long noOfPublicMethods = 0; | |
for (Entry<Long, Set<Entry<String, String>>> e : classAndMethodsByNoOfMethodOccurances.entrySet()) | |
{ | |
long noOfOccurances = e.getKey(); | |
Set<Entry<String, String>> classAndMethodNames = e.getValue(); | |
int noOfMethods = classAndMethodNames.size(); | |
noOfPublicMethods += noOfOccurances * classAndMethodNames.size(); | |
String methodNames; | |
if (noOfMethods > 10) | |
methodNames = ("(" + noOfMethods + ") methods"); | |
else | |
{ | |
methodNames = classAndMethodNames | |
.stream() | |
.collect(Collectors.groupingBy(Entry::getKey, Collectors.mapping(Entry::getValue, Collectors.toList()))) | |
.toString(); | |
methodNames = methodNames.substring(1, methodNames.length()); | |
} | |
System.out.printf(" %2d %-5s | %4d %-7s %s%n", noOfOccurances, | |
(noOfOccurances == 1 ? "time" : "times"), noOfMethods, (noOfMethods == 1 ? "method" : "methods"), | |
methodNames); | |
} | |
System.out.println("\nTotal number of methods: " + noOfPublicMethods); | |
} | |
/** | |
* <pre> | |
Overloaded | No. of Methods | |
----------------------------- | |
1 time | 7899 methods (7899) methods | |
2 times | 1169 methods (1169) methods | |
3 times | 198 methods (198) methods | |
4 times | 155 methods (155) methods | |
5 times | 10 methods java.io.PrintWriter=[write], java.nio.IntBuffer=[put], java.nio.FloatBuffer=[put], java.nio.DoubleBuffer=[put], java.io.Writer=[write], java.nio.ShortBuffer=[put], java.security.KeyStore=[getInstance], java.nio.LongBuffer=[put], java.text.NumberFormat=[format], java.nio.ByteBuffer=[put]} | |
6 times | 42 methods (42) methods | |
7 times | 3 methods java.nio.CharBuffer=[put], java.util.concurrent.ForkJoinPool=[submit], java.time.LocalDateTime=[of]} | |
8 times | 5 methods java.util.Arrays=[compareUnsigned, parallelPrefix, spliterator, stream], java.util.ResourceBundle=[getBundle]} | |
9 times | 8 methods java.io.PrintWriter=[append, print], java.io.PrintStream=[print], java.util.Arrays=[hashCode, toString], java.io.StringWriter=[append], java.lang.String=[valueOf], java.io.CharArrayWriter=[append]} | |
10 times | 4 methods java.io.PrintWriter=[println], java.io.PrintStream=[println], java.util.Arrays=[copyOf, copyOfRange]} | |
11 times | 1 method java.util.Map=[of]} | |
12 times | 3 methods java.util.List=[of], java.lang.AbstractStringBuilder=[insert], java.util.Set=[of]} | |
13 times | 1 method java.util.Spliterators=[spliterator]} | |
16 times | 1 method java.lang.AbstractStringBuilder=[append]} | |
18 times | 4 methods java.util.Arrays=[binarySearch, fill, parallelSort, sort]} | |
20 times | 3 methods java.util.Arrays=[compare, equals, mismatch]} | |
24 times | 2 methods java.lang.StringBuffer=[insert], java.lang.StringBuilder=[insert]} | |
29 times | 2 methods java.lang.StringBuffer=[append], java.lang.StringBuilder=[append]} | |
Total number of methods: 12240 | |
* </pre> | |
*/ | |
} | |
static void topWordsInJavaSourceFiles() throws IOException | |
{ | |
var pattern = Pattern.compile("[A-Z][A-Za-z]{3,}"); | |
long topN = 100; | |
try (var zipFile = new ZipFile("/usr/lib/jvm/java-11.0.1-openjdk-amd64/lib/src.zip")) | |
{ | |
var ai = new AtomicInteger(); | |
String[] fullPathArr = { null }; | |
Set<String> allPaths = new HashSet<String>(); | |
Set<String> franklinPaths = new HashSet<String>(); | |
Stream<String> wordsStream = zipFile.stream() | |
.flatMap(zipEntry -> | |
{ | |
// jdk.localedata/sun/util/resources/provider/LocaleDataProvider.java | |
var fullPath = zipEntry.getName(); | |
var firstSlash = fullPath.indexOf('/'); | |
var secondSlash = fullPath.indexOf('/', firstSlash + 1); | |
if (secondSlash == -1) // jdk.management/module-info.java | |
return Stream.empty(); | |
var packageName = fullPath.substring(firstSlash + 1, secondSlash); | |
if (!packageName.startsWith("java")) | |
return Stream.empty(); | |
if (!fullPath.endsWith(".java")) | |
return Stream.<String>empty(); | |
fullPathArr[0] = fullPath; | |
allPaths.add(fullPath); | |
ai.incrementAndGet(); | |
try | |
{ | |
return new BufferedReader(new InputStreamReader(zipFile.getInputStream(zipEntry))).lines(); | |
} | |
catch (IOException e) | |
{ | |
throw new UncheckedIOException(e); | |
} | |
}) | |
.flatMap(line -> pattern.matcher(line).results().map(MatchResult::group)) | |
.filter(word -> | |
{ | |
if (word.equals("Franklin")) | |
franklinPaths.add(fullPathArr[0]); | |
// Allow if more small letters than capitals | |
for (int i = 0, half = word.length() / 2, noOfSmallCase = 0; i < word.length(); i++) | |
{ | |
char c = word.charAt(i); | |
if (c >= 'a' && c <= 'z') | |
{ | |
noOfSmallCase++; | |
if (noOfSmallCase > half) | |
return true; | |
} | |
} | |
return false; | |
}); | |
Map<String, Long> repCountByWord = wordsStream | |
.collect(Collectors.groupingBy(Function.identity(), | |
() -> new TreeMap<>(), | |
Collectors.counting())); | |
franklinPaths.forEach(System.out::println); | |
System.out.println(franklinPaths.size()); | |
allPaths.removeAll(franklinPaths); | |
System.out.println("Missing Franklin: " + allPaths); | |
System.out.println("Number of Java classses: " + ai.get()); | |
System.out.printf("Total number of unique words matching pattern %s: %d%n%n", pattern.pattern(), repCountByWord.size()); | |
int maxWordLength = 0; | |
long maxRepCount = 0; | |
for (Entry<String, Long> entry : repCountByWord.entrySet()) | |
{ | |
maxWordLength = Math.max(maxWordLength, entry.getKey().length()); | |
maxRepCount = Math.max(maxRepCount, entry.getValue()); | |
} | |
maxWordLength = Math.min(maxWordLength, 30); | |
String format = "%-" + maxWordLength + "s %," + (("" + maxRepCount).length() + 1) + "d%n"; | |
Map<Long, Set<String>> wordsByRepCount = repCountByWord.entrySet().stream() | |
.collect(Collectors.groupingBy(Entry::getValue, | |
() -> new TreeMap<Long, Set<String>>(Comparator.reverseOrder()), | |
Collectors.mapping(Entry::getKey, Collectors.toCollection(TreeSet::new)))); | |
wordsByRepCount.entrySet().stream() | |
.limit(topN) | |
.forEachOrdered(entry -> | |
{ | |
Long wordCount = entry.getKey(); | |
Set<String> words = entry.getValue(); | |
for (String word : words) | |
System.out.printf(format, word, wordCount); | |
}); | |
} | |
} | |
static void topClassFilesBySize() throws IOException | |
{ | |
var basePath = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules"); | |
try (var ps = Files.walk(basePath)) | |
{ | |
Map<Long, List<String>> pathsBySize = ps | |
.map(path -> | |
{ | |
try | |
{ | |
// path modules/java.base/java/lang/Object.class | |
if (!path.getFileName().toString().endsWith(".class")) | |
return null; | |
String pkgStartName = path.getName(2).toString(); | |
if (!pkgStartName.startsWith("java")) | |
return null; | |
var pathWithoutModuleName = path.subpath(2, path.getNameCount()).toString().replace('/', '.'); | |
return Map.entry(Files.size(path), pathWithoutModuleName); | |
} | |
catch (IOException e) | |
{ | |
throw new UncheckedIOException(e); | |
} | |
}) | |
.filter(Objects::nonNull) | |
.collect(Collectors.groupingBy(Entry::getKey, | |
() -> new TreeMap<Long, List<String>>(Comparator.reverseOrder()), | |
Collectors.mapping(Entry::getValue, Collectors.toCollection(ArrayList::new)))); | |
System.out.println("-----------------"); | |
System.out.println(" Size | Classes"); | |
System.out.println("-----------------"); | |
pathsBySize.entrySet().stream() | |
.limit(10) | |
.forEach(entry -> | |
{ | |
var classNames = entry.getValue(); | |
classNames.sort(Comparator.naturalOrder()); | |
System.out.printf("%,6d | %s%n", entry.getKey(), String.join(", ", classNames)); | |
}); | |
} | |
/** | |
* <pre> | |
Size | Classes | |
----------------- | |
99,260 | java.awt.Component.class | |
82,650 | javax.swing.plaf.nimbus.NimbusDefaults.class | |
76,896 | javax.swing.JTable.class | |
64,480 | java.util.concurrent.ConcurrentHashMap.class | |
63,643 | java.math.BigDecimal.class | |
62,932 | java.util.Arrays.class | |
61,693 | javax.swing.JComponent.class | |
61,447 | java.util.concurrent.CompletableFuture.class | |
59,067 | java.math.BigInteger.class | |
58,990 | javax.swing.plaf.basic.BasicLookAndFeel.class | |
* </pre> | |
*/ | |
} | |
static void topClassFilesBySizeInclusingInnerClasses() throws IOException | |
{ | |
var basePath = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules"); | |
try (var ps = Files.walk(basePath)) | |
{ | |
Map<String, List<Entry<String, Long>>> classNameAndSizesByParentClassName = ps | |
.map(path -> | |
{ | |
try | |
{ | |
// path modules/java.base/java/lang/Object.class | |
if (!path.getFileName().toString().endsWith(".class")) | |
return null; | |
String pkgStartName = path.getName(2).toString(); | |
if (!pkgStartName.startsWith("java")) | |
return null; | |
var pathWithoutModuleName = path.subpath(2, path.getNameCount()).toString().replace('/', '.'); | |
var pathWithoutExtn = pathWithoutModuleName.substring(0, pathWithoutModuleName.length() - 6); | |
var dollarIndex = pathWithoutExtn.lastIndexOf('$'); | |
var parentClassName = dollarIndex == -1 ? pathWithoutExtn : pathWithoutExtn.substring(0, dollarIndex); | |
return Map.entry(parentClassName, Map.entry(pathWithoutExtn, Files.size(path))); | |
} | |
catch (IOException e) | |
{ | |
throw new UncheckedIOException(e); | |
} | |
}) | |
.filter(Objects::nonNull) | |
.collect(Collectors.groupingBy(Entry::getKey, | |
Collectors.mapping(Entry::getValue, Collectors.toList()))); | |
Map<Long, List<Entry<String, Long>>> classNameAnsSizesByTotalSize = classNameAndSizesByParentClassName.entrySet().stream() | |
.collect(Collectors.toMap( | |
entry -> entry.getValue().stream().mapToLong(Entry::getValue).sum(), | |
Entry::getValue, | |
(l1, l2) -> | |
{ | |
l1.addAll(l2); | |
return l1; | |
}, | |
() -> new TreeMap<Long, List<Entry<String, Long>>>(Comparator.reverseOrder()))); | |
// | |
System.out.println("-------------------"); | |
System.out.println(" Size | Classes"); | |
System.out.println("-------------------"); | |
classNameAnsSizesByTotalSize.entrySet().stream() | |
.limit(10) | |
.forEach(entry -> | |
{ | |
var classNames = entry.getValue(); | |
var max = Collections.max(classNames, Entry.comparingByKey()); | |
System.out.printf("%,8d | %s + %2d inner classes%n", entry.getKey(), max.getKey(), classNames.size() - 1); | |
}); | |
/** | |
* <pre> | |
Started | |
------------------- | |
Size | Classes | |
------------------- | |
250,052 | java.util.concurrent.ConcurrentHashMap + 53 inner classes | |
200,321 | java.util.Collections + 45 inner classes | |
154,180 | java.lang.invoke.BoundMethodHandle + 38 inner classes | |
139,571 | java.util.concurrent.CompletableFuture + 33 inner classes | |
135,156 | java.util.regex.Pattern + 58 inner classes | |
132,122 | java.awt.Component + 13 inner classes | |
124,450 | javax.swing.JTable + 23 inner classes | |
123,970 | java.time.format.DateTimeFormatterBuilder + 23 inner classes | |
111,738 | javax.swing.plaf.basic.BasicTreeUI + 21 inner classes | |
102,551 | javax.swing.plaf.basic.BasicTabbedPaneUI + 14 inner classes | |
Completed | |
* </pre> | |
*/ | |
} | |
} | |
static void topClassFilesByNoOfInnerClasses() throws IOException | |
{ | |
var basePath = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules"); | |
try (var ps = Files.walk(basePath)) | |
{ | |
Map<String, List<String>> innerClassesByParentClassName = ps | |
.map(path -> | |
{ | |
// path e.g. modules/java.base/java/lang/Object.class | |
if (!path.getFileName().toString().endsWith(".class")) | |
return null; | |
String pkgStartName = path.getName(2).toString(); | |
if (!pkgStartName.startsWith("java")) | |
return null; | |
var pathWithoutModuleName = path.subpath(2, path.getNameCount()).toString().replace('/', '.'); | |
var pathWithoutExtn = pathWithoutModuleName.substring(0, pathWithoutModuleName.length() - 6); | |
var dollarIndex = pathWithoutExtn.lastIndexOf('$'); | |
var parentClassName = dollarIndex == -1 ? pathWithoutExtn : pathWithoutExtn.substring(0, dollarIndex); | |
return Map.entry(parentClassName, pathWithoutExtn); | |
}) | |
.filter(Objects::nonNull) | |
.collect(Collectors.groupingBy(Entry::getKey, | |
Collectors.mapping(Entry::getValue, Collectors.toList()))); | |
Map<Integer, List<List<String>>> innerClassesByCount = innerClassesByParentClassName.entrySet().stream() | |
.collect(Collectors.groupingBy( | |
entry -> entry.getValue().size(), | |
() -> new TreeMap<Integer, List<List<String>>>(Comparator.reverseOrder()), | |
Collectors.mapping(Entry::getValue, Collectors.toList()))); | |
System.out.println("------------------------------------------------"); | |
System.out.println(" No Of Inner Classes | Parent Class "); | |
System.out.println("------------------------------------------------"); | |
innerClassesByCount.entrySet().stream() | |
.limit(10) | |
.forEach(entry -> | |
{ | |
var noOfInnerClasses = entry.getKey(); | |
for (List<String> innerClasses : entry.getValue()) | |
{ | |
var parentClass = Collections.min(innerClasses, Comparator.naturalOrder()); | |
System.out.printf("%21d | %s%n", noOfInnerClasses, parentClass); | |
} | |
}); | |
/** | |
* <pre> | |
------------------------------------------------ | |
No Of Inner Classes | Parent Class | |
------------------------------------------------ | |
77 | javax.imageio.plugins.tiff.BaselineTIFFTagSet | |
59 | java.util.regex.Pattern | |
58 | javax.imageio.plugins.tiff.ExifTIFFTagSet | |
54 | java.util.concurrent.ConcurrentHashMap | |
48 | java.beans.MetaData | |
46 | java.util.Collections | |
39 | java.lang.invoke.BoundMethodHandle | |
36 | java.util.stream.ReduceOps | |
34 | javax.swing.text.DefaultEditorKit | |
34 | java.util.concurrent.CompletableFuture | |
32 | javax.imageio.plugins.tiff.ExifGPSTagSet | |
* </pre> | |
*/ | |
} | |
} | |
static void publicStaticFields() throws Throwable | |
{ | |
var basePath = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules"); | |
try (var pathStream = Files.walk(basePath)) | |
{ | |
for (var path : (Iterable<Path>) pathStream::iterator) | |
{ | |
if (path.getNameCount() == 1) | |
System.out.println(path); | |
if (path.getNameCount() < 3) | |
continue; | |
String pkgStartName = path.getName(2).toString(); | |
if (!pkgStartName.equals("java")) | |
continue; | |
Path fileNamePath = path.getFileName(); | |
if (fileNamePath == null || !fileNamePath.toString().endsWith(".class")) | |
continue; | |
// path e.g. modules/java.base/java/lang/Object.class | |
var pathWithoutModuleName = path.subpath(2, path.getNameCount()).toString().replace('/', '.'); | |
var pathWithoutClassExtn = pathWithoutModuleName.substring(0, pathWithoutModuleName.lastIndexOf('.')); | |
Class<?> cls = Class.forName(pathWithoutClassExtn); | |
if (!Modifier.isPublic(cls.getModifiers())) | |
continue; | |
for (var field : cls.getFields()) | |
{ | |
if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers()) | |
&& field.getType().isArray()) | |
System.out.println(field); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment