서로 인접한 두 원소를 검사하여 정렬하는 알고리즘
구현 시뮬레이션
1. 각 Step의 첫번째 값을 고정
2. 고정된 값(파란색 칸)과 뒤에 있는 값(빨간색 칸)을 각각 비교하여 고정된 값보다 작은 값이 있다면 Swap
3. 각 Step이 끝나면, 고정된 값에 비교한 숫자 중 가장 작은 숫자가 옴

의문사항
Bubble Sort VS Selection Sort
Bubble Sort : 인접한 두 원소를 비교
Selection Sort : 최솟값을 구한 후 앞쪽의 값과 교환
➡ 강사님의 방식은 두 방식을 모두 사용한 것과 같음
조건
1. static method는 main에만 사용해라
2. int형 array를 return하고 int형 array를 받는 sort() method를 만들어라
import java.util.Arrays;
public class BubbleSort01 {
int[] sort(int[] arr) {
int temp;
for(int i = 0; i < arr.length; i++) {
for(int j = i + 1; j < arr.length; j++) {
if(arr[i] > arr[j]){
// swap
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
public static void main(String[] args) {
int[] arr = new int[]{7, 2, 3, 9, 28, 11};
// 오름차순 또는 내림차순으로 정렬하는 알고리즘을 짜보세요.
BubbleSort01 bubbleSort01 = new BubbleSort01();
int[] resultArr = bubbleSort01.sort(arr);
System.out.println(Arrays.toString(resultArr));
}
}
시간복잡도
버블 정렬은 이중 for문으로 구현되어 시간복잡도가 O(n^2)임
이번 Hospital Project는 Gradle 빌드를 하여 따로 프로젝트를 제작
왜 Gradle 빌드를 사용할까? ➡ Test Case를 작성하고 실행하기가 좋다.
Gradle 프로젝트 생성 방법
File ➡ New ➡ Project ➡ Build system : Gradle로 생성

어노테이션(annotaion)
@DisplayName을 통해 적어줌특정 Class의 Test Case를 만들고 싶을 때
Class명에 커서 위치 ➡ Alt + Enter ➡ Create Test

🔴 Test Case가 Test 디렉토리에 생성되지 않는 경우
Test 디렉토리가 없을 때 : src 하위에 Test 디렉토리 생성 ➡ Test 오른쪽 버튼 ➡ mark directory as ➡ Test Sources Root ➡ Test 디렉토리 하위에 java 디렉토리 생성 ➡ 이후 다시 Test Case 생성 시도
화면 Split
Class와 Test Case를 동시에 화면에 띄워서 효율적으로 작업
방법 : 작업 Tab의 java파일 오른쪽 버튼 ➡ Split Right, Split Down 중 선택하여 사용

서울시 병의원 위치 정보
해당 데이터는 서울시 병의원의 위치 정보를 가지고 있음
이 데이터 중 일부를 사용하여 MySql에서 활용 예정
| 변수명 | 데이터 설명 | 인덱스 |
|---|---|---|
| id | 기관ID | 0 |
| address | 주소 | 1 |
| district | 주소에서 시구 추출 | 1에서 추출 |
| category | 병원분류 | 2 |
| emergencyRoom | 응급실운영여부 | 6 |
| name | 기관명 | 10 |
| subdivision | 세부분과(기관명에서 추출) | 10에서 추출 |
고려사항
1. 데이터에서 parsing시, "문제 발생
str = str.replaceAll("\"", ""); : "를 공백으로 대체하여 오류 방지
2. district
생성자에서 작업
주소를 공백으로 나눈 후 시와 구만 가져와서 넣어줌
String[] splitted = this.address.split(" ");
this.district = String.format("%s %s", splitted[0], splitted[1]);
private String getSubdivision(String name) {
String[] subdivisions = {"소아과", "피부과", "성형외과", "정형외과", "척추", "교정", "산부인과", "관절", "봉합", "화상", "골절", "영유아", "안과", "가정의학과", "비뇨기과", "치과", "내과", "외과"};
for (String subdivision : subdivisions) {
if(name.contains(subdivision)){
return subdivision;
}
}
return "";
}
Parsing Test 코드
🟢 2개의 정보를 Test하기 위해 assertHospital 메소드 생성
➡ parsing 데이터와 실제 데이터가 맞는지 비교
String line1 = "\"A1120837\",\"서울특별시 금천구 벚꽃로 286 삼성리더스타워 111~114호 (가산동)\",\"C\",\"의원\",\"G099\",\"응급의료기관 이외\",\"2\",\"외과: 상시진료 내과는 당분간 휴진\",\"서울시 송파구 문정동 장지동 법조단지 위례 가락동 가락시장역 위치 삼성서울병원 외래교수 출신 구강외과 전문의 진료 진료과목 - 임플란트 치조골 뼈이식 수술 매복 사랑니 발치 턱관절 악관절 질환의 치료 교정 치료 및 기타 보존 보철(크라운 브릿지 인레이) 신경치료\",\"방이역 1번출구 바로옆 굿모닝 신한증권 뒷건물\",\"가산기대찬의원\",\"02-6267-2580\",\"02-920-5374\",\"1930\",\"1930\",\"1930\",\"1930\",\"1930\",\"1500\",\"1500\",\"1500\",\"0900\",\"0900\",\"0900\",\"0900\",\"0900\",\"0900\",\"1000\",\"1000\",\"085\",\"11\",\"126.88412249700781\",\"37.4803938036867\",\"2022-04-07 14:55:00.0\"";
String line2 = "\"A1117873\",\"서울특별시 관악구 신원로 38 5층 (신림동 청암빌딩)\",\"N\",\"치과의원\",\"G099\",\"응급의료기관 이외\",\"2\",\"대표번호1 지역번호 추가20170118150453\",\"서울시 송파구 문정동 장지동 법조단지 위례 가락동 가락시장역 위치 삼성서울병원 외래교수 출신 구강외과 전문의 진료 진료과목 - 임플란트 치조골 뼈이식 수술 매복 사랑니 발치 턱관절 악관절 질환의 치료 교정 치료 및 기타 보존 보철(크라운 브릿지 인레이) 신경치료\",\"서월치안센터 인근 청암빌딩 5층\",\"가로수치과의원\",\"02-882-2750\",\"02-920-5374\",\"1900\",\"2100\",\"1900\",\"2100\",\"1900\",\"1400\",\"1500\",\"1500\",\"1000\",\"1000\",\"0930\",\"1400\",\"1000\",\"1000\",\"1000\",\"1000\",\"087\",\"76\",\"126.92937673003041\",\"37.48191798611885\",\"2022-01-07 14:54:55.0\"";
private void assertHospital(Hospital hospital, String eId, String eAddress, String eDistrict, String eCategory, int eEmergencyRoom, String eName, String eSubdivision) {
// Id 파싱 확인
Assertions.assertEquals(eId, hospital.getId());
// Address 파싱 확인
Assertions.assertEquals(eAddress, hospital.getAddress());
// District 파싱 확인
Assertions.assertEquals(eDistrict, hospital.getDistrict());
// Category 파싱 확인
Assertions.assertEquals(eCategory, hospital.getCategory());
// EmergencyRoom 파싱 확인
Assertions.assertEquals(eEmergencyRoom, hospital.getEmergencyRoom());
// Name 파싱 확인
Assertions.assertEquals(eName, hospital.getName());
// Subdivision 파싱 확인
Assertions.assertEquals(eSubdivision, hospital.getSubdivision());
}
// annotation
@Test
@DisplayName("Parsing doing well")
void hospitalParsing() {
HospitalParser hospitalParser = new HospitalParser();
// ctrl + tab
// line 1
assertHospital(hospitalParser.parse(this.line1),
"A1120837", "서울특별시 금천구 벚꽃로 286 삼성리더스타워 111~114호 (가산동)",
"서울특별시 금천구", "C", 2, "가산기대찬의원", "");
// line 2
assertHospital(hospitalParser.parse(this.line2),
"A1117873", "서울특별시 관악구 신원로 38 5층 (신림동 청암빌딩)",
"서울특별시 관악구", "N", 2, "가로수치과의원", "치과");
}
Sql Insert문 형식 만드는 코드
public String getSqlInsertQuery() {
this.address = this.address.replace("'", "");
String query = "INSERT INTO `likelion-db`.`seoul_hospital` (`id`,`address`,`district`,`category`,`emergency_room`,`name`,`subdivision`) " +
"VALUES (\"" + this.id + "\",\"" + this.address + "\",\"" + this.district + "\",\"" + this.category + "\"," +
this.emergencyRoom + ",\"" + this.name + "\",";
if(this.subdivision != null) {
query += "\"" + this.subdivision + "\");";
} else {
query += "\");";
}
return query;
}
🟢 Hospital Class 내부에 위치하여 생성자로 받은 변수 데이터를 넣어서 Insert문 형식인 String을 리턴
.sql 파일 저장
List<String> lines = new ArrayList<>();
for (Hospital hospital : hospitals) {
lines.add(hospital.getSqlInsertQuery());
}
String sqlFilename = "hospital_insert.sql";
hospitalLineReader.createANewFile(sqlFilename);
hospitalLineReader.writeLines(lines, sqlFilename);
🟢 Hospital 형식의 데이터를 getSqlInsertQuery 메소드를 거쳐 String 배열 생성
🟢 sqlFilename을 가진 파일 생성 후 파일 쓰기
Hospital : 데이터를 저장할 형식을 담고 있는 클래스 public class Hospital {
private String id;
private String address;
private String district;
private String category;
private int emergencyRoom;
private String name;
private String subdivision;
public Hospital(String id, String address) {
this.id = id;
this.address = address;
}
public Hospital(String id, String address, String category, int emergencyRoom, String name, String subdivision) {
this.id = id;
this.address = address;
String[] splitted = this.address.split(" ");
this.district = String.format("%s %s", splitted[0], splitted[1]);
this.category = category;
this.emergencyRoom = emergencyRoom;
this.name = name;
this.subdivision = subdivision;
}
public String getSqlInsertQuery() {
this.address = this.address.replace("'", "");
String query = "INSERT INTO `likelion-db`.`seoul_hospital` (`id`,`address`,`district`,`category`,`emergency_room`,`name`,`subdivision`) " +
"VALUES (\"" + this.id + "\",\"" + this.address + "\",\"" + this.district + "\",\"" + this.category + "\"," +
this.emergencyRoom + ",\"" + this.name + "\",";
if(this.subdivision != null) {
query += "\"" + this.subdivision + "\");";
} else {
query += "\");";
}
return query;
}
public String getId() {
return id;
}
public String getAddress() {
return address;
}
public String getDistrict() {
return district;
}
public String getCategory() {
return category;
}
public int getEmergencyRoom() {
return emergencyRoom;
}
public String getName() {
return name;
}
public String getSubdivision() {
return subdivision;
}
}
HospitalParser : 데이터를 parsing하여 Hospital 객체를 생성하는 클래스public class HospitalParser implements Parser<Hospital> {
private String getSubdivision(String name) {
String[] subdivisions = {"소아과", "피부과", "성형외과", "정형외과", "척추", "교정", "산부인과", "관절", "봉합", "화상", "골절", "영유아", "안과", "가정의학과", "비뇨기과", "치과", "내과", "외과"};
for (String subdivision : subdivisions) {
if(name.contains(subdivision)){
return subdivision;
}
}
return "";
}
@Override
public Hospital parse(String str) {
// "에 대한 에러 방지
str = str.replaceAll("\"", "");
String[] splitted = str.split(",");
String name = splitted[10];
String subDivision = getSubdivision(name);
return new Hospital(splitted[0], splitted[1], splitted[2], Integer.parseInt(splitted[6]), name, subDivision);
}
}
LineReader : 데이터를 읽기, 파일 생성, 파일 쓰기 메소드를 담고있는 클래스import com.line.parser.Parser;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class LineReader<T> {
Parser<T> parser;
boolean isRemoveColumnName = true;
public LineReader(Parser<T> parser) {
this.parser = parser;
}
// 첫행 받는여부를 생성자로 받을 수 있음
public LineReader(Parser<T> parser, boolean isRemoveColumnName) {
this.parser = parser;
this.isRemoveColumnName = isRemoveColumnName;
}
List<T> readlines(String filename) throws IOException {
List<T> result = new ArrayList<>();
BufferedReader br = new BufferedReader(new FileReader(filename));
String str;
// 첫 행 날리기
if(isRemoveColumnName) {
br.readLine();
}
while((str = br.readLine()) != null) {
result.add(parser.parse(str));
}
return result;
}
public void createANewFile(String filename) throws IOException {
File file = new File(filename);
file.createNewFile();
System.out.println("파일 생성 되었는지?:" + file.exists());
}
public void writeLines(List<String> lines, String filename) throws IOException{
File file = new File(filename);
FileWriter fileWriter = new FileWriter(file);
for (int i = 0; i < lines.size(); i++) {
fileWriter.write(lines.get(i) + "\n");
}
fileWriter.flush();
fileWriter.close();
System.out.println("success");
}
}
Main : 파일을 읽어서 sql파일을 생성하는 Main 클래스import com.line.domain.Hospital;
import com.line.parser.HospitalParser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws IOException {
LineReader<Hospital> hospitalLineReader = new LineReader<>(new HospitalParser());
String filename = "D:\\고관운 자료\\멋쟁이사자처럼\\백엔드스쿨 2기 교본\\220919\\4주차\\서울시 병의원 위치 정보.csv";
List<Hospital> hospitals = hospitalLineReader.readlines(filename);
System.out.println(hospitals.size());
List<String> lines = new ArrayList<>();
for (Hospital hospital : hospitals) {
lines.add(hospital.getSqlInsertQuery());
}
String sqlFilename = "hospital_insert.sql";
hospitalLineReader.createANewFile(sqlFilename);
hospitalLineReader.writeLines(lines, sqlFilename);
}
}
HospitalParserTest : 각 변수마다 잘 Parsing 했는지 테스트, sql insert문으로 잘 전처리했는지 테스트하는 Test 클래스import com.line.domain.Hospital;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class HospitalParserTest {
String line1 = "\"A1120837\",\"서울특별시 금천구 벚꽃로 286 삼성리더스타워 111~114호 (가산동)\",\"C\",\"의원\",\"G099\",\"응급의료기관 이외\",\"2\",\"외과: 상시진료 내과는 당분간 휴진\",\"서울시 송파구 문정동 장지동 법조단지 위례 가락동 가락시장역 위치 삼성서울병원 외래교수 출신 구강외과 전문의 진료 진료과목 - 임플란트 치조골 뼈이식 수술 매복 사랑니 발치 턱관절 악관절 질환의 치료 교정 치료 및 기타 보존 보철(크라운 브릿지 인레이) 신경치료\",\"방이역 1번출구 바로옆 굿모닝 신한증권 뒷건물\",\"가산기대찬의원\",\"02-6267-2580\",\"02-920-5374\",\"1930\",\"1930\",\"1930\",\"1930\",\"1930\",\"1500\",\"1500\",\"1500\",\"0900\",\"0900\",\"0900\",\"0900\",\"0900\",\"0900\",\"1000\",\"1000\",\"085\",\"11\",\"126.88412249700781\",\"37.4803938036867\",\"2022-04-07 14:55:00.0\"";
String line2 = "\"A1117873\",\"서울특별시 관악구 신원로 38 5층 (신림동 청암빌딩)\",\"N\",\"치과의원\",\"G099\",\"응급의료기관 이외\",\"2\",\"대표번호1 지역번호 추가20170118150453\",\"서울시 송파구 문정동 장지동 법조단지 위례 가락동 가락시장역 위치 삼성서울병원 외래교수 출신 구강외과 전문의 진료 진료과목 - 임플란트 치조골 뼈이식 수술 매복 사랑니 발치 턱관절 악관절 질환의 치료 교정 치료 및 기타 보존 보철(크라운 브릿지 인레이) 신경치료\",\"서월치안센터 인근 청암빌딩 5층\",\"가로수치과의원\",\"02-882-2750\",\"02-920-5374\",\"1900\",\"2100\",\"1900\",\"2100\",\"1900\",\"1400\",\"1500\",\"1500\",\"1000\",\"1000\",\"0930\",\"1400\",\"1000\",\"1000\",\"1000\",\"1000\",\"087\",\"76\",\"126.92937673003041\",\"37.48191798611885\",\"2022-01-07 14:54:55.0\"";
private void assertHospital(Hospital hospital, String eId, String eAddress, String eDistrict, String eCategory, int eEmergencyRoom, String eName, String eSubdivision) {
// Id 파싱 확인
Assertions.assertEquals(eId, hospital.getId());
// Address 파싱 확인
Assertions.assertEquals(eAddress, hospital.getAddress());
// District 파싱 확인
Assertions.assertEquals(eDistrict, hospital.getDistrict());
// Category 파싱 확인
Assertions.assertEquals(eCategory, hospital.getCategory());
// EmergencyRoom 파싱 확인
Assertions.assertEquals(eEmergencyRoom, hospital.getEmergencyRoom());
// Name 파싱 확인
Assertions.assertEquals(eName, hospital.getName());
// Subdivision 파싱 확인
Assertions.assertEquals(eSubdivision, hospital.getSubdivision());
}
// annotation
@Test
@DisplayName("Parsing doing well")
void hospitalParsing() {
HospitalParser hospitalParser = new HospitalParser();
// ctrl + tab
// line 1
assertHospital(hospitalParser.parse(this.line1),
"A1120837", "서울특별시 금천구 벚꽃로 286 삼성리더스타워 111~114호 (가산동)",
"서울특별시 금천구", "C", 2, "가산기대찬의원", "");
// line 2
assertHospital(hospitalParser.parse(this.line2),
"A1117873", "서울특별시 관악구 신원로 38 5층 (신림동 청암빌딩)",
"서울특별시 관악구", "N", 2, "가로수치과의원", "치과");
}
@Test
@DisplayName("insert쿼리를 잘 만드는지 test")
void makeSqlQueryTest() {
HospitalParser hospitalParser = new HospitalParser();
Hospital hospital = hospitalParser.parse(this.line1);
String sql = "INSERT INTO `likelion-db`.`seoul_hospital` (`id`,`address`,`district`,`category`,`emergency_room`,`name`,`subdivision`) " +
"VALUES (\"A1120837\",\"서울특별시 금천구 벚꽃로 286 삼성리더스타워 111~114호 (가산동)\",\"서울특별시 금천구\",\"C\"," + 2 + ",\"가산기대찬의원\",\"\");";
Assertions.assertEquals(sql, hospital.getSqlInsertQuery());
}
}

