每次将Point添加到Arraylist(Java)时运行循环

时间:2016-04-21 11:09:05

标签: java arraylist

我目前在以下课程中工作,一个带有while循环的绘图程序,从客户端向服务器发送包:

FragmentPagerAdapter

因为我的while循环在每个循环中持续睡眠(100)。这可以按预期工作,但是为了有效,我希望每次在ArrayList位置更改某些内容时都会运行循环,因此它不会发送不相关的包。

我想要实现的目标:

public class TCPClient extends JPanel {


    public static ArrayList<Point> location = new ArrayList<>();

    private JTextArea consoleOutput = new JTextArea(1,20);

    public void addComponentToPane(Container pane) {
        consoleOutput.setEditable(false);
    }

    public TCPClient() {
        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                location.add(e.getPoint());
            }
        });

        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                location.add(e.getPoint());
                repaint();
            }
        });
        setPreferredSize(new Dimension(800, 500));
        setBackground(Color.WHITE);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        if(location.isEmpty()){
            return;
        }

        Point p = location.get(0);
        for (int i = 1; i < location.size(); i++) {
            Point q = location.get(i);
            g.drawLine(p.x, p.y, q.x, q.y);
            p = q;
        }
    }

    public static void main(String argv[])  throws Exception {

        InetAddress SERVERIP = InetAddress.getLocalHost();

        JFrame frame = new JFrame("Drawing with friends");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new TCPClient(), BorderLayout.CENTER);

        JTextArea IPadress = new JTextArea(1,20);
        IPadress.setEditable(false);
        IPadress.append("DEVICE IP: " + SERVERIP.getHostAddress());
        frame.add(IPadress, BorderLayout.SOUTH);

        frame.setSize(new Dimension(800,600));
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setVisible(true);

            while(true) {
                    try {
                        //BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
                        Socket clientSocket = new Socket("localhost", 9000);

                        ObjectOutputStream outToServer = new ObjectOutputStream(clientSocket.getOutputStream());

                        //ObjectInputStream inFromServer = new ObjectInputStream(clientSocket.getInputStream());

                        outToServer.writeObject(location);

                        outToServer.flush();
                        clientSocket.close();

                        Thread.sleep(100);

                    } catch (SocketException e) {
                        System.err.println(e.toString());         
            }
        }
    }
}

4 个答案:

答案 0 :(得分:1)

您需要将所有访问权限(readwrite)包装到您的变量位置,并将synchronized块包含notify(),以防您更新它,然后在您的循环中,您将sleep替换为下一个wait

修改位置

synchronized (location) {
    location.add(e.getPoint());
    location.notify();
}

读取位置访问权限:

synchronized (location) {
    if(location.isEmpty()){
        return;
    }

    Point p = location.get(0);
    for (int i = 1; i < location.size(); i++) {
        Point q = location.get(i);
        g.drawLine(p.x, p.y, q.x, q.y);
        p = q;
    }
}

同步块内的最终循环:

synchronized (location) {
    while(true) {
        //BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
        Socket clientSocket = new Socket("localhost", 9000);

        ObjectOutputStream outToServer = new ObjectOutputStream(clientSocket.getOutputStream());

        //ObjectInputStream inFromServer = new ObjectInputStream(clientSocket.getInputStream());

        outToServer.writeObject(location);

        outToServer.flush();
        clientSocket.close();
        location.wait();
    }
}

答案 1 :(得分:1)

基本上,你应该使用某种BlockingQueue,这将允许你的&#34;套接字&#34;线程到&#34;阻止&#34;在等待新的Point到达时。

您应该单独发送每个List,而不是发送整个Point,这样可以随着点数的增加而节省时间。

由于队列的性质,你可以继续为它添加更多的点和&#34; socket&#34;线程可以按照自己的步调处理它们,这将允许队列在用户绘制时增加大小,但允许&#34; socket&#34;线程在用户停止时赶上,没有你做任何特别的事情。

以下示例使用Thread.sleep在队列中处理每个点之间产生人为延迟以用于演示目的,显然,您不应该使用它(Thread.sleep),但它证明了上述几点,线程将在您停止绘制后继续将队列中的点转储到标准输出

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    private BlockingQueue<Point> queue;

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                queue = new LinkedBlockingQueue<>();

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TCPClient(queue));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                Thread t = new Thread(new Consumer(queue));
                t.setDaemon(true);
                t.start();
            }
        });
    }

    public class TCPClient extends JPanel {

        private JTextArea consoleOutput = new JTextArea(1, 20);
        private Queue<Point> queue;
        private List<Point> cache;

        public TCPClient(Queue<Point> queue) {
            this.queue = queue;
            cache = new ArrayList<>(25);
            addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    queue.add(e.getPoint());
                    cache.add(e.getPoint());
                }
            });

            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    queue.add(e.getPoint());
                    cache.add(e.getPoint());
                    repaint();
                }
            });
            setPreferredSize(new Dimension(800, 500));
            setBackground(Color.WHITE);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            if (cache.isEmpty()) {
                return;
            }

            Point p = cache.get(0);
            for (int i = 1; i < cache.size(); i++) {
                Point q = cache.get(i);
                g.drawLine(p.x, p.y, q.x, q.y);
                p = q;
            }
        }
    }

    public class Consumer implements Runnable {

        private BlockingQueue<Point> queue;

        public Consumer(BlockingQueue<Point> queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Point p = queue.poll(Long.MAX_VALUE, TimeUnit.DAYS);
                    if (p != null) {
                        System.out.println("-> Got " + p);
                        Thread.sleep(125);
                    }
                } catch (InterruptedException ex) {
                }
            }
        }

    }
}

答案 2 :(得分:0)

不要让其他对象直接修改您的ArrayList,而是将其设为私有,并创建一个getter,一个setter,以及(因为您正在使用集合)加法器和移除方法。

在这些内容中,您可以通知程序的其他部分,ArrayList已更改,您需要发送数据包。

答案 3 :(得分:0)

您可以创建自己的CallbackArrayList,其范围为ArrayList。 添加抽象回调方法,例如onAddedonRemoved

覆盖您要监视的ArrayList的那些方法,并在里面调用您的回调方法,具体取决于结果。

abstract class CallbackArrayList<T> extends ArrayList<T> {
  public abstract void onAddSuccess(T object);
  public abstract void onAddFailure(T object);
  @Override
  public boolean add(T object) {
    boolean success = super.add(object);
    if(success) {
      onAddSuccess(object);
    }
    else {
      onAddFailure(object);
    }
    return success;
  }
}

然后在分配列表时,您可以

location = new CallbackArrayList<>() {
  @Override
  public void onAddSuccess(Point object) {
    // handle send information to server
  }
  @Override
  public void onAddFailure(Point object) {
    // handle failure
  }
};

每当你调用location.add(e.getPoint())时,都会调用其中一个回调方法。