Модераторы: LSD, AntonSaburov

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Коллекции и дерево. Что посоветуете ? 
:(
    Опции темы
ci5
Дата 5.11.2011, 15:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 36
Регистрация: 24.12.2008

Репутация: нет
Всего: нет



Здравствуйте! Стоит задача создать дерево, каждый узел которого содержит: id name hash child parrent. Реализовать нужно:вставка в нужное место, удаление ветки, её расщепление, остальные легко реализуемые. 
 Сортировка тут не нужна, по сути у нас только имя главное в дереве из значений. 
 Собственно какие вижу проблемы: 
 1. не пойму как быть тут с id. В задании у нас возможно удалять как ветвь, так и отдельные элементы дерева. Тоесть если нужно сделать "расщепление", то надо прошлые ссылки детей присвоить к новому, более высокому родителю, который был родителем расщепленного. Но при этом при всём у нас id удалённого элемента будет недоступен. Как тут поступить ? Как идея, вести полный список id и при удалении одного, делать недоступными id и удалённого, и его детей, выдавая детям удалённого родителя новые id, которые выдаются исходя из общих id (sizeId). Может есть какое-то более разумное решение ? 
 2. Какую коллекцию лучше использовать ? Количество элементов заранее неизвестно. Сортировки по возрастанию и убыванию никакой. Сказали что вроде тут надо использовать LinkedList и использовать встроенный итератор для вставки в определённое место. Но я никак не могу себе представить себе такой подход. Это какая-то архитектура дерева в ширину будет, а не в ширину и глубину. То есть у нас по сути будет всего один элемент, у которого будет ссылка next на голову, хотя если смотреть на дерево, то таких элементов может быть довольно много. Вот многомерные массивы можно представить как дерево, только кол-во элементов определено, а вот тут никак не получается. 
 Что касается самих полей:
Код

import java.util.*; 
    public class Tree { 
        private static int sizeId; 
        class Node extends LinkedList{ 
            String name; 
            int id; 
            Node child; 
            Node parrent; 
            int hash; 
        }

Я вот как-то так это вижу. Подскажите куда копать. 
 Заранее спасибо.
PM MAIL   Вверх
dorogoyIV
Дата 5.11.2011, 17:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1503
Регистрация: 26.3.2007

Репутация: 3
Всего: 46



создаешь класс со своими полями: id name hash child parrent
в этом классе реализуешь метод toString() - это будет отображаться в дереве.
пишешь модель дерева на основе своего класса...
потом уже с моделью дерева можно работать - вставлять, удалять, ...
 smile 
PM MAIL   Вверх
math64
Дата 5.11.2011, 18:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 8
Всего: 72



Код

public class Tree {
  public class Node {
    int id;
    String Name;
    Node parent;
    List<Node> childs; // Используемый List может быть любой и даже меняться в зависимости от надобности
    int hash;
    // Методы для работы с узлом дерева по вкусу
  }
  List<Node> roots; // Узлы у которых parent = null;
  HashMap<int,Node> nodesById; // Для поиска по id
  // Методы для работы с деревом по вкусу
}


Добавлено через 3 минуты и 41 секунду
не HashMap, a Map - реализацию Map тоже можно менять по вкусу.
PM   Вверх
dorogoyIV
Дата 5.11.2011, 20:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1503
Регистрация: 26.3.2007

Репутация: 3
Всего: 46



Код

import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;

public class Main extends JFrame
                  implements ActionListener
{
 private JTree tree = new JTree();
 private JButton jb = new JButton("button");
 private JButton del = new JButton("del");
 private DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");

 public Main()
 {
  setBounds(100, 100, 400, 300);
  setDefaultCloseOperation(3);

  DefaultMutableTreeNode node1 = new DefaultMutableTreeNode(new MyClass(1, "one"));
  DefaultMutableTreeNode node2 = new DefaultMutableTreeNode(new MyClass(2, "two"));
  DefaultMutableTreeNode node3 = new DefaultMutableTreeNode(new MyClass(3, "three"));
  root.add(node1);
  root.add(node2);
  node2.add(node3);
  tree.setModel(new MyTreeModel(root));
  add(tree);
  add(jb, "South");
  jb.addActionListener(this);
  add(del, "East");
  del.addActionListener(this);
 }

 public void actionPerformed(ActionEvent e)
 {
  if(e.getSource().equals(jb))
   getData();
  if(e.getSource().equals(del))
   deleteNode();
 }

 private void deleteNode()
 {
  // пробуем удалить ноду
  MutableTreeNode node = (MutableTreeNode)tree.getLastSelectedPathComponent();
  node.removeFromParent();
  tree.updateUI();
 }

 private void getData()
 {
  // попробуем получить поля класса
  DefaultMutableTreeNode node = (DefaultMutableTreeNode)tree.getLastSelectedPathComponent();

  if(!node.equals(root))
  {
   MyClass mc = (MyClass)node.getUserObject();
   System.out.println("id = " + mc.getID() + " name = " + mc.getName());
  }
 }

 public static void main(String [] args)
 {
  SwingUtilities.invokeLater(new Runnable()
  {
   public void run()
   {
    new Main().setVisible(true);
   }
  });
 }
}

class MyClass
{
 private int ID;
 private String NAME;

 MyClass(int id, String name)
 {
  this.ID = id;
  this.NAME = name;
 }

 public int getID()
 {
  return ID;
 }

 public String getName()
 {
  return NAME;
 }

 public String toString()
 {
  return "ID = " + ID + " NAME = " + NAME;
 }
}

class MyTreeModel implements TreeModel
{
 Object root;

 MyTreeModel(Object root)
 {
  this.root = root;
 }

 public void addTreeModelListener(TreeModelListener l){}
 public void removeTreeModelListener(TreeModelListener l){}
 public void valueForPathChanged(TreePath path, Object newValue){}

 public Object getChild(Object parent, int index)
 {
  Object o = null;
  DefaultMutableTreeNode node = (DefaultMutableTreeNode)parent;

  for(int i = 0; i < node.getChildCount(); i++)
   if(i == index)
    o = node.getChildAt(i);
  return o;
 }

 public int getChildCount(Object parent) 
 {
  int count = 0;
  DefaultMutableTreeNode node = (DefaultMutableTreeNode)parent;

  if(!node.isLeaf())
   return node.getChildCount();
  else
   return 0;
 }

 public int getIndexOfChild(Object parent, Object child)
 {
  int x = 0;
  DefaultMutableTreeNode node = (DefaultMutableTreeNode)parent;

  for(int i = 0; i < node.getChildCount(); i++)
   if(node.getChildAt(i).equals(child))
    x = i;

  return x;
 }

 public Object getRoot() 
 {
  return root;
 }

 public boolean isLeaf(Object node) 
 {
  DefaultMutableTreeNode leaf = (DefaultMutableTreeNode)node;
  int count = leaf.getChildCount();

  if(count > 0)
   return false;
  else
   return true;
 }
}

PM MAIL   Вверх
ci5
Дата 6.11.2011, 20:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 36
Регистрация: 24.12.2008

Репутация: нет
Всего: нет



Спасибо за комментарии. По ним: 
dorogoyIV, спасибо конечно за код, но с awt и swing я еще дел не имел, поэтому эту задачу думаю выполнить в консольном приложении.
math64, вы писали:
>List<Node> roots; // Узлы у которых parent = null;
я так понимаю это вершины наших деревьев будут. А не проще их просто зациклить на себя? к примеру создается head при создании объекта, это будет наша вершина и ссылаться просто на себя в parrent. 

И еще, с коллекциями еще не приходилось работать. В примитивных типах я так понимаю там используются обертки для работы с коллекциями. Там ничего для понимания сложного нету. А вот тут как получится ? Мне придется получается переопределять все методы LinkedList и описывать, или создавать класс в соответствии с интерфейсом List ? Извиняюсь, если вопрос глупый. Но как-то не могу понять этого момента. 
PM MAIL   Вверх
math64
Дата 7.11.2011, 08:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 8
Всего: 72



Пользуйтесь стандартными классами списков  - Вам их вполне достаточно. Но используйте везде List<Node>, кроме конструктора при необходимости new LinkedList<Node>() можно заменить на new ArrayList<Node>() и наоборот.
По поводу List<Node> roots. Вам нужно уметь удалять любой узел, в том числе и корневой. Тогда (если он удаляется без дочерних элементов), бывшие дочерние элементы станут несколько корневыми, поэтому не один Node root, а  список.

PM   Вверх
ci5
Дата 9.11.2011, 17:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 36
Регистрация: 24.12.2008

Репутация: нет
Всего: нет



Спасибо. Уже можно сказать получил дерево, но есть некоторые непонятки. 
Для интерфейса Map использую LinkedHashMap. Не пойму как получить ключ по значению. Значение по ключу понятно как получать, но возможно ли наоборот? В английском не силен, но вроде стандартных методов как я понял на это дело нету ? 
PM MAIL   Вверх
math64
Дата 9.11.2011, 19:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 8
Всего: 72



Если Map заполнена правильно (ключом является Node.id), то чтобы получить ключ по node, нужно посмотреть на поле node.id.
Если заполнена неправильно - то только перебором всех ключей.
Если у node меняется id, нужно сначала удалить его из map, используя старый id в качестве ключа, изменить id и добавить в map по новому id. Предполагается, что у всех Node уникальный id, отличный от других.
PM   Вверх
ci5
  Дата 9.11.2011, 20:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 36
Регистрация: 24.12.2008

Репутация: нет
Всего: нет



На счет Map. Тоесть в карте вообще можно не перебирать элементы, если коллекция правильно составлена ? (головной элемент сказали сделать неудаляемым)
И еще вопрос если можно, связанный с сериализацией. Не могу понять в чем ошибка.
Код

public class Tree implements iTree {
    private int sizeId=0;
    private Node head=new Node();//корень дерева
    private Map<Integer,Node> nodesById=new LinkedHashMap<Integer,Node>(); 
    
    private class Node implements Serializable, Cloneable  {
        int id; 
        String name;
        Node parent;
        List<Node> childs; 
        int hash;
    }   
...
//конструктор создания дерева
    public Tree(String name){
         Node aTree=new Node();
         aTree.id=0;
         aTree.name=name;
         aTree.parent=null;
         aTree.childs=new LinkedList();
         aTree.hash=aTree.hashCode();
         head=aTree;
         nodesById.put(aTree.id, aTree);
    }
    
    //метод добавления узла дерева
    @Override
    public void addNode(int id,String name)throws TreeIndexOutOfBoundsException {
         if (checkId(id)){ //проверка id на границы
            Node aNode=new Node();//создаем объект Node
            aNode.id=++sizeId; //присваиваем ему новый id
            nodesById.put(aNode.id, aNode); //кладем в нашу карту новую пару <ключ,значение>
            aNode.name=name; //присваиваем имя
            aNode.parent=getNode(id); //находим родителя и присваиваем ссылку на него
            aNode.childs=new LinkedList();//создаем список детей для созданного объекта
            aNode.hash=aNode.hashCode(); //вычисляем hash
            getNode(id).childs.add(aNode); //у родителя в его список детей кладем наш созданный    объект
         }
         else
             System.out.println("Bad id Index");
             //или всё же throw new TreeIndexOutOfBoundsException() ? 
    }
...
    private Node getNode(int id) throws TreeIndexOutOfBoundsException{
        if ((id<0)||(id>sizeId)) throw new TreeIndexOutOfBoundsException(); //если выходим за границы id индекса, то выбрасываем исключение
        for(Object key : nodesById.keySet()) { //ищем в нашей карте ключей ключ с индексом id
           if (key==id)//если нашли нужный индекс и поле Node в этом ключе не равно null, выводим name
               return nodesById.get(key); 
        }
        throw new TreeIndexOutOfBoundsException();  
    }

Код

    public static void main(String[] args) 
    throws InterruptedException, IOException, ClassNotFoundException {
        // TODO code application logic here
        Tree aTree=new Tree("derevo");
        aTree.addGroupNode(0);
        aTree.getElements();
        
        //Сериализация----------------------------------------------------------
        System.out.println("\nРабота с сериализацией...");
        ObjectOutputStream sout = new ObjectOutputStream(new FileOutputStream("c:/forSerializable.bin"));
        sout.writeObject(aTree);
        sout.close();            
        //Десериализация--------------------------------------------------------     
        ObjectInputStream sin = new ObjectInputStream(new FileInputStream("c:/forSerializable.bin"));
        Tree serTree = (Tree)sin.readObject();            
        sin.close(); 
        System.out.println("вывод сериализованного дерева из файла");
        System.out.println();
        for (int i=0;i<serTree.getSize();i++)
            serTree.getElements();


Работает это всё хорошо, но только не десериализация (а может и сериализация тоже)
Цитата
run:

Please, enter the child's Name(Parent id=0) or enter 'quit' to exit: vetka1
Element was added successfully

Please, enter the child's Name(Parent id=0) or enter 'quit' to exit: vetka2
Element was added successfully

Please, enter the child's Name(Parent id=0) or enter 'quit' to exit: vetka3
Element was added successfully

Please, enter the child's Name(Parent id=0) or enter 'quit' to exit: quit
element name=derevo; id=0
element name=vetka1; id=1
element name=vetka2; id=2
element name=vetka3; id=3

Работа с сериализацией...
вывод сериализованного дерева из файла

Exception in thread "main" abstracttree.TreeIndexOutOfBoundsException: bad currentIndex
    at abstracttree.Tree.getNode(Tree.java:132)


Ругается в getNode на >        throw new TreeIndexOutOfBoundsException();  
По сути, я же выводил дерево до сериализации. Тут её сделал и для проверки вызвал у сериализованного объекта метод getSize, который отработал как надо. Боюсь я где-то с map что-то не так делаю. (TreeIndexOutOfBoundsException - public class TreeIndexOutOfBoundsException extends RuntimeException)

забыл сказать. это у меня 2 класса.  в главном, где main 
        aTree.addGroupNode(0); - просто сделал цикл на addNode, а ноль это id корня


Это сообщение отредактировал(а) ci5 - 9.11.2011, 20:43
PM MAIL   Вверх
math64
Дата 10.11.2011, 07:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 8
Всего: 72



В addNode я бы вернул созданный Node. В случае ошибки вернуть null или throw исключение.
В getNode перебор индексов не нужен - достаточно
Код

 Node node = nodesById.get(id);
 if (node != null) return node;

С сериализацией я особо не разбирался, но, возможно, удобнее сериализировать в xml?
Так как xml - текстовый файл, легко проверить, правильно ли прошла сериализация.

Добавлено через 4 минуты и 50 секунд
Кстати, при map сериализировать не надо - после десериализации нужно пробежаться по дереву заполнить map, при сериализации лучше избегать дублирования информации.
PM   Вверх
ci5
Дата 10.11.2011, 14:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 36
Регистрация: 24.12.2008

Репутация: нет
Всего: нет



Спасибо большое! Учту. Про сериализацию в xml... Вроде же сериализация сохраняет я так понимаю как бы массив байт. Если я его загоню в xml, то как я опять же получу в нём информацию, лёгкую для прочтения? Хотя думаю почитаю про xml и всё встанет на свои места.
Ошибку в коде нашел. сравнивал объекты через ==, вместо equals  smile 
PM MAIL   Вверх
math64
Дата 10.11.2011, 15:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 8
Всего: 72



xml - текстовый файл вида:
Код

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tree>
<tree>
  <node id = "0" name="root"  hash="0">
    <node id="1" name="child1" hash="0"/>
    <node id="2" name="child2" hash="0">
     <node id="3" name="child3" hash="0"/>
    </node>
  </node>
</tree>

Легко проверить правильно ли сохранилось.
PM   Вверх
ci5
  Дата 11.11.2011, 15:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 36
Регистрация: 24.12.2008

Репутация: нет
Всего: нет



Прошу помочь еще в одном деле. Не первый час мучаюсь с удалением ветки вместе с её элементами и не могу довести до полностью рабочего состояния.
Код

  public void deleteNode(int id)throws TreeIndexOutOfBoundsException{
        if (checkId(id))
          if (id!=0){ //проверка id на границы. дополнительно проверяем не является ли корнем
            Node del=nodesById.get(id);
            ListIterator iter=del.childs.listIterator();
            Node aNode = nodesById.get(id);
            nodesById.remove(id);
            while (iter.hasNext()){ //если дети у объекта есть, то
                del=(Node)iter.next(); //копируем адрес в del
                nodesById.remove(del.id); //и удаляем этот же ключ с этим id в нашей карте 
            }            
            aNode.childs.clear(); //очистка у самого узла его коллекции childs
            iter=aNode.parent.childs.listIterator();
            while(true){ //ищем у parrent в его списке childs объект, который мы удаляем и удаляем его
             if (iter.hasNext())
                     if (iter.next()==(aNode)){
                         iter.remove();
                         aNode.parent=null;
                         break;
                     }
            }
        }
        else
              throw new TreeIndexOutOfBoundsException();
    }

Боюсь тут много излишков кода. К примеру я получаю дерево такое, как на рисунке (внутри узла его id)
user posted image
после чего удаляю второй по номеру id элемент. Он должен удалить мне все, кроме 0, 1, 3. Но он удаляет мне все, кроме 0,1,3, 8. При этом nodesById.size выдаёт мне что наша карта содержит 9 элементов. Я понимаю откуда эти 9 элементов взялись. Просто детей от id=5 мы не удаляли, а только затерли его родителя. В общем в карте map у нас содержатся элементы 0,1,3,8 реальных, а остальные 5 с Node=null, поэтому они и не выводятся. Что тут делать ? чистить постоянно коллекцию на нулевые ноды или как ? и так уже запутался с вроде бы как не сложным делом. Да и элемент с id=8 тоже не понятно почему вылазит. Разве что только iter.next сразу прыгает на второй элемент. 
PM MAIL   Вверх
math64
Дата 11.11.2011, 21:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 8
Всего: 72



Не нужны лишние проверки.
В то же время других не хватает.
Нужно проверять результат nodesById.get(id) на null
Для удаления элемента из списка не нужно перебирать элементы - вызывай просто remove().
Если узел удаляется с дочерними элементами, вызови рекурсивно удаление дочерних элементов.
Примерно так:
Код

public void deleteNode(int id) throwsTreeIndexOutOfBoundsException{
  Node del=nodesById.get(id);
  if (del == null)
    throw new TreeIndexOutOfBoundsException();
  delete(del);
}
public static final boolean ROOT_IS_NOT_DELETABLE = true;
public void deleteNode(Node del) throwsTreeIndexOutOfBoundsException{
  if (del == null)
    throw new TreeIndexOutOfBoundsException();
  if (ROOT_IS_NOT_DELETABLE && del == root)
    throw new TreeIndexOutOfBoundsException();
  for(Node child : del.childs) // Итератор спрятан в for - так удобнее
    deleteNode(child);
  Node parent = del.parent;
  if (parent != null)
    parent.childs.remove(del);
  nodesById.remove(del.id);
  // это делать не обязательно - мусорщик почистит
  // del.parent = null;
  // del.childs.clear();
  // del.childs = null;
}


PM   Вверх
ci5
  Дата 12.11.2011, 12:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 36
Регистрация: 24.12.2008

Репутация: нет
Всего: нет



В целом тут всё понятно и прозрачно. 
В коде на итераторе я ловлю исключение 
Цитата
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:761)
    at java.util.LinkedList$ListItr.next(LinkedList.java:696)
    at abstracttree.Tree.deleteNode(Tree.java:107)
    at abstracttree.Tree.deleteNode(Tree.java:97)
    at abstracttree.AbstractTree.main(AbstractTree.java:25)

Я так понимаю проблема в том, что изменяется коллекция, а так как итератор по ней бегает в данный момент, то и выбрасывается исключении о том, что коллекция изменилась. 
(в вашем коде подправил только delete(del); - 5 стр на deleteNode(del); )
Первое что на ум приходит это получать массив из ссылок, которые лежат в коллекции на объекты, но это опять же загромождать программу и понижать её скорость. Как поступить, подскажите пожалуйста. 
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0980 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.