У меня не было возможности протестировать этот код, но общая идея состоит в том, чтобы перебрать дерево через цикл for-each над дочерними элементами. Мы сохраняем текущий путь внутри строки, добавляя текущее имя на каждом шаге рекурсии. Затем при попадании в лист мы добавляем текущий путь в список.
public ArrayList<String> buildListOfPaths(Node tree) {
ArrayList<String> list = new ArrayList<String>();
String str = "";
traverse(tree, list, str);
return list;
}
// The idea on how to iterate the elements comes from:
// https://stackoverflow.com/a/19338057
public void traverse(Node root, ArrayList<String> list, String str){
// we know it's a leaf so we can add this path to the list
if (root.getChildren() == null) {
list.add(str + root.name);
return;
} else {
for(Node each : root.getChildren()){
traverse(each, list, str + each.name);
}
}
}
Да, способ, которым Вы записали их, эквивалентен.
Однако Вы не должны действительно ожидать потока Супердемона для завершения. Когда основной поток закончит выполняться основной (), тот поток выходы, но JVM не будет. JVM будет продолжать бежать, пока последний поток недемона не выходит из своего метода выполнения.
Например,
public class KeepRunning {
public static void main(String[] args) {
Superdaemon d = new Superdaemon();
d.start();
System.out.println(Thread.currentThread().getName() + ": leaving main()");
}
}
class Superdaemon extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName() + ": starting");
try { Thread.sleep(2000); } catch(InterruptedException e) {}
System.out.println(Thread.currentThread().getName() + ": completing");
}
}
Вы будете видеть вывод:
main: leaving main()
Thread-0: starting
Thread-0: completing
Другими словами, основной поток приходит первым, затем вторичный поток завершается и выходы JVM.
Проблема - то, что книги как JCIP защищают это, мы используем Исполнителей для Потоков запусков. Таким образом, я стараюсь изо всех сил не использовать Thread.start (). Я не уверен, выбрал ли я обязательно конкретный способ сделать вещи только на основе простоты. Должна быть более убедительная причина, нет?
Убедительная причина использовать java.util.concurrent состоит в том, что многопоточное программирование очень хитро. Java предлагает инструменты тому (Потоки, синхронизируемые и энергозависимые ключевые слова), но это не означает, что можно безопасно использовать их непосредственно, не стреляя себе в ногу: Или слишком много синхронизации, приводящей к ненужным узким местам и мертвым блокировкам, или также меньше, приводя к ошибочному поведению из-за условий состязания).
С java.util.concurrent Вы получаете ряд утилит (записанный экспертами) для наиболее распространенных шаблонов использования, которые можно просто использовать, не волнуясь, что Вы разобрались в материале низкого уровня.
В Вашем особом случае, тем не менее, я действительно не совсем вижу, почему Вам нужен отдельный Поток вообще, Вы могли бы также использовать основной:
public static void main( String[] args )
{
Runnable t = new Superdaemon();
t.run();
}
Исполнители предназначены для задач, которые Вы хотите выполнить в фоновом режиме (когда у Вас есть несколько параллельных задач или когда Ваш текущий поток может продолжить делать что-то еще).
Future.get () получит будущий ответ от асинхронного вызова. Это также заблокируется, если вызов еще не был завершен. Это во многом как соединение потока.
http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Future.html
Sort'a. Future.get()
для того, чтобы иметь поток, уходят и вычисляют что-то и затем возвращают его вызывающему потоку безопасным способом. Это работало бы если get
никогда не возвращался. Но, я придерживался бы с join
звоните, поскольку это более просто и никакой Executer наверху (не, что было бы все так очень).
Править
Это похоже ExecutorService.submit(Runnable)
предназначается, чтобы сделать exectly, чего Вы делаете попытку. Это просто возвращается null
когда Runnable
завершается. Интересный.