Ahm*_*nie 13 java algorithm recursion nio java-8
我在编写算法时遇到问题,以帮助我扫描文件系统并查找某个类的所有子类.
细节:
我有一个应用程序扫描外部应用程序时使用nio
Files.walk()
检索我检查"扩展SuperClass",同时读取文件,如果单词退出,我在列表中添加类名称,如下所示:
List<String> subclasses = new ArrayList<>();
Files.walk(appPath)
.filter(p->Files.isRegularFile(p) && p.toString()
.endsWith(".java")).forEach(path -> {
try {
List<String> lines = Files.readAllLines(path);
Pattern pattern = Pattern.compile("\\bextends SuperClass\\b");
Matcher matcher = pattern
.matcher(lines.stream()
.collect(Collectors.joining(" ")));
boolean isChild = matcher.find();
if(isChild) subclasses.add(path.getFileName().toString());
}catch (IOException e){
//handle IOE
}
Run Code Online (Sandbox Code Playgroud)
上面的问题是它只获得直接的子类,SuperClass
但我需要检索所有直接和间接的子类.我想到递归,因为我不知道有多少子类,SuperClass
但我无法实现任何合理的实现.
笔记:
SuperClass
有extends
存在的位置编辑:
我使用以下正则表达式比较名称和导入,以确保即使在相同名称的情况下不同的包输出是正确的:
Pattern pattern = Pattern.compile("("+superClasss.getPackage()+")[\\s\\S]*(\\bextends "+superClass.getName()+"\\b)[\\s\\S]");
Run Code Online (Sandbox Code Playgroud)
我也尝试过:
Pattern pattern = Pattern.compile("\\bextends "+superClass.getName()+"\\b");
Run Code Online (Sandbox Code Playgroud)
但是也有一些缺少的子类,我相信下面的代码会跳过一些检查,并且不能完全发挥作用:
public static List<SuperClass> getAllSubClasses(Path path, SuperClass parentClass) throws IOException{
classesToDo.add(baseClass);
while(classesToDo.size() > 0) {
SuperClass superClass = classesToDo.remove(0);
List<SuperClass> subclasses = getDirectSubClasses(parentPath,parentClass);
if(subclasses.size() > 0)
classes.addAll(subclasses);
classesToDo.addAll(subclasses);
}
return classes;
Run Code Online (Sandbox Code Playgroud)
}
任何帮助真的很感激!
编辑2
我还注意到另一个问题,就是当我检测到subclass
我得到的文件名currentPath.getFileName()
可能是也可能不是子类名时,因为子类可能是同一文件中的nested
非公开class
的.
Hol*_*ger 13
我强烈建议解析编译的类文件而不是源代码.由于这些类文件已经过优化以供机器处理,因此消除了源代码文件处理的许多复杂性和极端情况.
因此,使用ASM库构建完整的类层次结构树的解决方案如下所示:
public static Map<String, Set<String>> getClassHierarchy(Path root) throws IOException {
return Files.walk(root)
.filter(p->Files.isRegularFile(p) && isClass(p.getFileName().toString()))
.map(p -> getClassAndSuper(p))
.collect(Collectors.groupingBy(Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toSet())));
}
private static boolean isClass(String fName) {
// skip package-info and module-info
return fName.endsWith(".class") && !fName.endsWith("-info.class");
}
private static Map.Entry<String,String> getClassAndSuper(Path p) {
final class CV extends ClassVisitor {
Map.Entry<String,String> result;
public CV() {
super(Opcodes.ASM5);
}
@Override
public void visit(int version, int access,
String name, String signature, String superName, String[] interfaces) {
result = new AbstractMap.SimpleImmutableEntry<>(
Type.getObjectType(name).getClassName(),
superName!=null? Type.getObjectType(superName).getClassName(): "");
}
}
try {
final CV visitor = new CV();
new ClassReader(Files.readAllBytes(p)).accept(visitor, ClassReader.SKIP_CODE);
return visitor.result;
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
Run Code Online (Sandbox Code Playgroud)
作为奖励,resp.为了创建一些测试用例,以下方法增加了为运行时类源构建层次结构的能力:
public static Map<String, Set<String>> getClassHierarchy(Class<?> context)
throws IOException, URISyntaxException {
Path p;
URI clURI = context.getResource(context.getSimpleName()+".class").toURI();
if(clURI.getScheme().equals("jrt")) p = Paths.get(URI.create("jrt:/modules"));
else {
if(!clURI.getScheme().equals("file")) try {
FileSystems.getFileSystem(clURI);
} catch(FileSystemNotFoundException ex) {
FileSystems.newFileSystem(clURI, Collections.emptyMap());
}
String qn = context.getName();
p = Paths.get(clURI).getParent();
for(int ix = qn.indexOf('.'); ix>0; ix = qn.indexOf('.', ix+1)) p = p.getParent();
}
return getClassHierarchy(p);
}
Run Code Online (Sandbox Code Playgroud)
然后,你可以做到
Map<String, Set<String>> hierarchy = getClassHierarchy(Number.class);
System.out.println("Direct subclasses of "+Number.class);
hierarchy.getOrDefault("java.lang.Number", Collections.emptySet())
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
得到
Direct subclasses of class java.lang.Number
java.lang.Float
java.math.BigDecimal
java.util.concurrent.atomic.AtomicLong
java.lang.Double
java.lang.Long
java.util.concurrent.atomic.AtomicInteger
java.lang.Short
java.math.BigInteger
java.lang.Byte
java.util.concurrent.atomic.Striped64
java.lang.Integer
Run Code Online (Sandbox Code Playgroud)
要么
Map<String, Set<String>> hierarchy = getClassHierarchy(Number.class);
System.out.println("All subclasses of "+Number.class);
printAllClasses(hierarchy, "java.lang.Number", " ");
Run Code Online (Sandbox Code Playgroud)
private static void printAllClasses(
Map<String, Set<String>> hierarchy, String parent, String i) {
hierarchy.getOrDefault(parent, Collections.emptySet())
.forEach(x -> {
System.out.println(i+x);
printAllClasses(hierarchy, x, i+" ");
});
}
Run Code Online (Sandbox Code Playgroud)
要得到
All subclasses of class java.lang.Number
java.lang.Float
java.math.BigDecimal
java.util.concurrent.atomic.AtomicLong
java.lang.Double
java.lang.Long
java.util.concurrent.atomic.AtomicInteger
java.lang.Short
java.math.BigInteger
java.lang.Byte
java.util.concurrent.atomic.Striped64
java.util.concurrent.atomic.LongAdder
java.util.concurrent.atomic.LongAccumulator
java.util.concurrent.atomic.DoubleAdder
java.util.concurrent.atomic.DoubleAccumulator
java.lang.Integer
Run Code Online (Sandbox Code Playgroud)