Sibainu Relax Room

愛犬の柴犬とともに過ごす部屋

JAVAでクイックソート3

おっ出来たか。JAVAはじめてだからちょっと心配したぞという顔をしている柴犬です。

概要

クイックソートのコードとCSVファイル書き出しのコードを追加します。

CSVファイルの読み込みから、ソート、書き出しまで2~3秒ほどで完了しました。

私の予想より早いです。

JAVAはコードも理解しやすく、このケースを見る限り実行速度も速いので、いろいろ使えそうですが、JAVAそのもの開発者の方向性により利便性が変わることが気になります。

今日も、この本にお世話になっています。

ソートコードの追加

ソートのコードを追加した全コードです。

copy

import java.awt.FileDialog;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class test extends JFrame implements ActionListener {
	FileDialog fd;
	JLabel lb;

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

	public test() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(600 , 240);
		setLayout(new GridLayout(3,1));

		
		JButton load = new JButton("LOAD");
		load.addActionListener(this);
		getContentPane().add(load);
		JButton save = new JButton("SAVE");
		save.addActionListener(this);
		getContentPane().add(save);
		
		lb = new JLabel();
		getContentPane().add(lb);
		
		setVisible(true);
	}

	public void actionPerformed(ActionEvent e) {
		String fullpath;
		//クリックしたボタンにより処理を分ける
		if (e.getActionCommand() == "LOAD" ) {
			fd = new FileDialog(this , "ファイルを開く" , FileDialog.LOAD);
		} else {
			fd = new FileDialog(this , "ファイルの保存" , FileDialog.SAVE);
		}
		//ファイルダイヤログの表示
		fd.setVisible(true);
		// キャンセル対応
		if (fd.getDirectory() != null) {
			fullpath =fd.getDirectory() + fd.getFile();
			lb.setText(fullpath);
			readcsv(fullpath);
		} else {
			lb.setText("");
		}
	}
	
	public void readcsv(String fullpath) {
		try {
			FileInputStream fis = new FileInputStream(fullpath);
			InputStreamReader isr = new InputStreamReader(fis, "SJIS");
			BufferedReader br = new BufferedReader(isr);
			
	        	String rl;
	        	int cr = 0;
	        	ArrayList<String[]> rlf = new ArrayList<String[]>();
	        	while ((rl = br.readLine()) != null) {
	        		rlf.add(rl.split(","));
	        		cr += 1;
			}

			br.close();

			QuickSort(rlf, 0, cr - 1);

		} catch(IOException e) {
			lb.setText(e.toString());
		}
	
	}
	
	public void QuickSort(ArrayList<String[]> d, int left, int right) {
		// リストdのleftからrightまでの間のデータ列をクイックソートします。
		if (left>=right) {
	    		return;
		}
	    
		int pivot = getposi(d, left, right);
		// 再帰
		QuickSort(d, left, pivot - 1);  // ピボットより左側をクイックソート
		QuickSort(d, pivot + 1, right); // ピボットより右側をクイックソート

	}

	
	public int getposi(ArrayList<String[]> d, int left, int right) {

		// ピボットの値をLeftとRightの中間インデックスの値とし、
		// 左端と交換します
		int pivot = (left + right) / 2;
		swap(d.get(pivot), d.get(left));

		int l = left + 1;
		int r = right;

		while (1 > 0) {
			while(leftcomp(d.get(l), d.get(left), l, right)) { l++; }
			while(rightcomp(d.get(r), d.get(left))) { r--; }
			if (l>=r) {
	    			// 交差ならば終了してループを抜けて交換します
	    			break;
			}
	    		// 交差前であるから交換して次に進みます
	    		swap(d.get(l), d.get(r));
		}
		// 交差したので交換します
		swap(d.get(left), d.get(r));
		// ピボットの正しい位置を返します
		return r;
	}
	
	public void swap(String[] p, String[] l) {

		String temp =  l[0];
		l[0] = p[0];
		p[0] = temp;
		temp = l[1];
		l[1] = p[1];
		p[1] = temp;

	}
	
	// Leftスタートの進行条件を判定する関数
	public boolean leftcomp(String[] a, String[] p, int l, int right) {
		int res;
		res = a[0].compareTo(p[0]);
		if (res == 0) {
			res = a[1].compareTo(p[1]);
		}
		return (res <= 0 && l < right);
	}

	// Rightスタートの進行条件を判定する関数
	public boolean rightcomp(String[] b, String[] p) {
		int res;
		res = b[0].compareTo(p[0]);
		if (res == 0) {
			res = b[1].compareTo(p[1]);
		}
		return (res > 0);
	}

}

コードの実行

上のコードでCSVファイルの読み込みソートを実行してみました。

2022年12月27日投稿の「RUSTでクイックソート3」と最初の1行だけですけど一致しています。

上手くソートできているようで、書き出しが楽しみです。

CSVファイルの書き出しのための修正

readcsv 関数の中の QuickSort の実行文の後に次の1行を挿入しました。

ただし、WEB上のコード表記は円記号¥がバックスラッシュで置き換わっています。エクリプスなどのエディターの中では円記号¥です。

writecsv(rlf, "C:\\Users\\都合の良いホルダー\\output.csv");

copy

	public void readcsv(String fullpath) {
		try {
			FileInputStream fis = new FileInputStream(fullpath);
			InputStreamReader isr = new InputStreamReader(fis, "SJIS");
			BufferedReader br = new BufferedReader(isr);
			
	        	String rl;
	        	int cr = 0;
	        	ArrayList<String[]> rlf = new ArrayList<String[]>();
	        	while ((rl = br.readLine()) != null) {
	        		rlf.add(rl.split(","));
	        		cr += 1;
			}

			br.close();

			QuickSort(rlf, 0, cr - 1);

			// \はエスケープ文字にします
			writecsv(rlf, "C:\\Users\\都合の良いホルダー\\output.csv");

		} catch(IOException e) {
			lb.setText(e.toString());
		}
	
	}

CSVファイルを書き出す関数 writecsv を追加しました。

copy

	public void writecsv(ArrayList<String[]> d, String fullpath) {
		try {
			File f = new File(fullpath);
			if (f.exists()) {
				f.createNewFile();
			}
			FileOutputStream fos = new FileOutputStream(f);
			OutputStreamWriter osw = new OutputStreamWriter(fos,"SJIS");
			BufferedWriter bw = new BufferedWriter(osw);
			PrintWriter pw = new PrintWriter(bw);

			//ファイルに文字列を書き込む
			for (String[] s: d) {
				pw.println(s[0] + "," + s[1]);
			}
			//ファイルをクローズする
			pw.close();
			
			lb.setText("完了しました。");
			
		} catch(IOException e) {
			lb.setText(e.toString());
		}
	}

ソート後のCSVファイルの作成

コードの実行

上のコードを実行してみました。

とりあえず無事完了したようです。

作成したCSVファイルを見る

RUSTでクイックソート3 の結果と合っています。目的が達成されてよかったです。

全コード

今回の最終の全コードです。

ただし、WEB上のコード表記は円記号¥がバックスラッシュで置き換わっています。エクリプスなどのエディターの中では円記号¥です。

copy

import java.awt.FileDialog;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class test10 extends JFrame implements ActionListener {
	FileDialog fd;
	JLabel lb;

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

	public test10() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(600 , 240);
		setLayout(new GridLayout(3,1));

		
		JButton load = new JButton("LOAD");
		load.addActionListener(this);
		getContentPane().add(load);
		JButton save = new JButton("SAVE");
		save.addActionListener(this);
		getContentPane().add(save);
		
		lb = new JLabel();
		getContentPane().add(lb);
		
		setVisible(true);
	}

	public void actionPerformed(ActionEvent e) {
		String fullpath;
		//クリックしたボタンにより処理を分ける
		if (e.getActionCommand() == "LOAD" ) {
			fd = new FileDialog(this , "ファイルを開く" , FileDialog.LOAD);
		} else {
			fd = new FileDialog(this , "ファイルの保存" , FileDialog.SAVE);
		}
		//ファイルダイヤログの表示
		fd.setVisible(true);
		// キャンセル対応
		if (fd.getDirectory() != null) {
			fullpath =fd.getDirectory() + fd.getFile();
			lb.setText(fullpath);
			readcsv(fullpath);
		} else {
			lb.setText("");
		}
	}
	
	public void readcsv(String fullpath) {
		try {
			FileInputStream fis = new FileInputStream(fullpath);
			InputStreamReader isr = new InputStreamReader(fis, "SJIS");
			BufferedReader br = new BufferedReader(isr);
			
	        	String rl;
	        	int cr = 0;
	        	ArrayList<String[]> rlf = new ArrayList<String[]>();
	        	while ((rl = br.readLine()) != null) {
	        		rlf.add(rl.split(","));
	        		cr += 1;
			}

			br.close();

			QuickSort(rlf, 0, cr - 1);

			// \はエスケープ文字にします
			writecsv(rlf, "C:\\Users\\都合の良いホルダー\\output.csv");

		} catch(IOException e) {
			lb.setText(e.toString());
		}
	
	}
	
	public void QuickSort(ArrayList<String[]> d, int left, int right) {
		// リストdのleftからrightまでの間のデータ列をクイックソートします。
		if (left>=right) {
	    		return;
		}
	    
		int pivot = getposi(d, left, right);
		// 再帰
		QuickSort(d, left, pivot - 1);  // ピボットより左側をクイックソート
		QuickSort(d, pivot + 1, right); // ピボットより右側をクイックソート

	}

	
	public int getposi(ArrayList<String[]> d, int left, int right) {

		// ピボットの値をLeftとRightの中間インデックスの値とし、
		// 左端と交換します
		int pivot = (left + right) / 2;
		swap(d.get(pivot), d.get(left));

		// 左端はピボットなので 1 を加えます
		int l = left + 1;
		int r = right;
		// 無限ループにして交差までループします
		while (1 > 0) {
			while(leftcomp(d.get(l), d.get(left), l, right)) { l++; }
			while(rightcomp(d.get(r), d.get(left))) { r--; }
			if (l>=r) {
	    			// 交差ならば終了してループを抜けて交換します
	    			break;
			}
	    		// 交差前であるから交換して次に進みます
	    		swap(d.get(l), d.get(r));
		}
		// 交差したので交換します
		swap(d.get(left), d.get(r));
		// ピボットの正しい位置を返します
		return r;
	}
	
	public void swap(String[] p, String[] l) {

		String temp =  l[0];
		l[0] = p[0];
		p[0] = temp;
		temp = l[1];
		l[1] = p[1];
		p[1] = temp;

	}
	
	// Leftスタートの進行条件を判定する関数
	public boolean leftcomp(String[] a, String[] p, int l, int right) {
		int res;
		res = a[0].compareTo(p[0]);
		if (res == 0) {
			res = a[1].compareTo(p[1]);
		}
		return (res <= 0 && l < right);
	}

	// Rightスタートの進行条件を判定する関数
	public boolean rightcomp(String[] b, String[] p) {
		int res;
		res = b[0].compareTo(p[0]);
		if (res == 0) {
			res = b[1].compareTo(p[1]);
		}
		return (res > 0);
	}

	public void writecsv(ArrayList<String[]> d, String fullpath) {
		try {
			File f = new File(fullpath);
			if (f.exists()) {
				f.createNewFile();
			}
			FileOutputStream fos = new FileOutputStream(f);
			OutputStreamWriter osw = new OutputStreamWriter(fos,"SJIS");
			BufferedWriter bw = new BufferedWriter(osw);
			PrintWriter pw = new PrintWriter(bw);

			//ファイルに文字列を書き込む
			for (String[] s: d) {
				pw.println(s[0] + "," + s[1]);
			}
			//ファイルをクローズする
			pw.close();
			
			lb.setText("完了しました。");
			
		} catch(IOException e) {
			lb.setText(e.toString());
		}
	}

}

取りかかって3日かかりましたが何とかできたようです。

なんとなく JAVA が分ったような気分になりました。

今回はこれまでとします。

文字セット

JAVAの公式HP「サポートされているエンコーディング」に一覧表があります。

https://docs.oracle.com/javase/jp/1.5.0/guide/intl/encoding.doc.html

この表から判断すると「SJIS」か「MS932」を指定すればよいようです。OSがWindowsなら「MS932」とするのが最適かもしれません。