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

每次将点添加到Arraylist时运行循环(Java),java,arraylist,Java,Arraylist,我目前在以下课程中工作,这是一个带有while循环的绘画程序,用于将包从客户端发送到服务器: public class TCPClient extends JPanel { public static ArrayList<Point> location = new ArrayList<>(); private JTextArea consoleOutput = new JTextArea(1,20); public void addComp

我目前在以下课程中工作,这是一个带有while循环的绘画程序,用于将包从客户端发送到服务器:

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());         
            }
        }
    }
}

不要让其他对象直接修改您的
ArrayList
,而是将其设置为私有,并创建一个getter、setter以及一个adder和remover方法(因为您使用的是集合)


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

您需要使用
notify()将对变量位置的任何访问(
read
write
)包装到一个
synchronized
块中
如果您对其进行了更新,然后在循环中用
wait
替换
sleep
,如下所示:

位置修改:

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();
    }
}
对位置的读取权限:

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();
    }
}

您可以创建自己的
CallbackArrayList
,它扩展了
ArrayList
。 添加抽象回调方法,例如
onAdded
onRemoved

覆盖要监视的
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;
  }
}
抽象类CallbackArrayList扩展了ArrayList{
公开摘要无效(T对象);
公开摘要失效(T对象);
@凌驾
公共布尔加法(T对象){
布尔成功=super.add(对象);
如果(成功){
onAddSuccess(对象);
}
否则{
onAddFailure(对象);
}
回归成功;
}
}
然后在分配列表时,您可以执行以下操作

location = new CallbackArrayList<>() {
  @Override
  public void onAddSuccess(Point object) {
    // handle send information to server
  }
  @Override
  public void onAddFailure(Point object) {
    // handle failure
  }
};
location=newcallbackarraylist(){
@凌驾
公共无效onAddSuccess(点对象){
//句柄将信息发送到服务器
}
@凌驾
公共无效onAddFailure(点对象){
//处理失败
}
};

无论何时调用
location.add(e.getPoint())
,之后都会调用其中一个回调方法。

基本上,您应该使用某种
阻塞队列
,这将允许您的“套接字”线程在等待新的
到达时“阻塞”

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

由于队列的性质,您可以不断向其添加更多点,“套接字”线程可以按自己的速度处理这些点,这将允许队列随着用户的绘制而增大,但允许“套接字”线程在用户停止时赶上,而无需您做任何特殊的操作

下面的示例使用
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) {
                }
            }
        }

    }
}
导入java.awt.Color;
导入java.awt.Dimension;
导入java.awt.EventQueue;
导入java.awt.Graphics;
导入java.awt.Point;
导入java.awt.event.MouseAdapter;
导入java.awt.event.MouseEvent;
导入java.awt.event.MouseMotionAdapter;
导入java.util.ArrayList;
导入java.util.List;
导入java.util.Queue;
导入java.util.concurrent.BlockingQueue;
导入java.util.concurrent.LinkedBlockingQueue;
导入java.util.concurrent.TimeUnit;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.JTextArea;
导入javax.swing.UIManager;
导入javax.swing.UnsupportedLookAndFeelException;
公开课考试{
私有阻塞队列;
公共静态void main(字符串[]args){
新测试();
}
公开考试(){
invokeLater(新的Runnable(){
@凌驾
公开募捐{
试一试{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}catch(ClassNotFoundException |实例化Exception | IllegalacessException |不支持ookandfeelException ex){
例如printStackTrace();
}
队列=新的LinkedBlockingQueue();
JFrame=新JFrame(“测试”);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(新的TCPClient(queue));
frame.pack();
frame.setLocationRelativeTo(空);
frame.setVisible(true);
线程t=新线程(新使用者(队列));
t、 setDaemon(true);
t、 start();
}
});
}
公共类TCPClient扩展了JPanel{
专用JTextArea控制台输出=新的JTextArea(1,20);
专用队列;
私有列表缓存;
公共TCP客户端(队列){
this.queue=队列;
cache=newarraylist(25);
addMouseListener(新的MouseAdapter(){
@凌驾
公共无效鼠标按下(MouseEvent e){
添加(例如getPoint());
cache.add(如getPoint());
}
});
addMouseMotionListener(新的MouseMotionAdapter(){
@凌驾
公共无效鼠标标记(鼠标事件e){
添加(例如getPoint());
cache.add(如getPoint());
重新油漆();
}
});
设置首选尺寸(新尺寸(800500));
挫折地面(颜色:白色);
}
@凌驾
受保护组件(图形g){
超级组件(g);
if(cache.isEmpty()){
返回;
}
p点=cache.get(0);
对于(int i=1;i