import java.util.Date;
public class person {
Date birthDt;
int age;
String name;
String address_country;
String address_city;
String address_detail;
String hobby;
char gender;
double weight;
double height;
public Date getBirthDt() {
return birthDt;
}
public void setBirthDt(Date birthDt) {
this.birthDt = birthDt;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress_country() {
return address_country;
}
public void setAddress_country(String address_country) {
this.address_country = address_country;
}
public String getAddress_city() {
return address_city;
}
public void setAddress_city(String address_city) {
this.address_city = address_city;
}
public String getAddress_detail() {
return address_detail;
}
public void setAddress_detail(String address_detail) {
this.address_detail = address_detail;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
}
person class가 있고,
import java.util.Comparator;
public class PeopleCompareUtil implements Comparator<Object> {
/**
* Compares its two arguments for order. Returns a negative integer,
* zero, or a positive integer as the first argument is less than, equal
* to, or greater than the second.<p>
* <p>
* The implementor must ensure that {@link Integer#signum
* signum}{@code (compare(x, y)) == -signum(compare(y, x))} for
* all {@code x} and {@code y}. (This implies that {@code
* compare(x, y)} must throw an exception if and only if {@code
* compare(y, x)} throws an exception.)<p>
* <p>
* The implementor must also ensure that the relation is transitive:
* {@code ((compare(x, y)>0) && (compare(y, z)>0))} implies
* {@code compare(x, z)>0}.<p>
* <p>
* Finally, the implementor must ensure that {@code compare(x,
* y)==0} implies that {@code signum(compare(x,
* z))==signum(compare(y, z))} for all {@code z}.
*
* @param o1 the first object to be compared.
* @param o2 the second object to be compared.
* @return a negative integer, zero, or a positive integer as the
* first argument is less than, equal to, or greater than the
* second.
* @throws NullPointerException if an argument is null and this
* comparator does not permit null arguments
* @throws ClassCastException if the arguments' types prevent them from
* being compared by this comparator.
* @apiNote It is generally the case, but <i>not</i> strictly required that
* {@code (compare(x, y)==0) == (x.equals(y))}. Generally speaking,
* any comparator that violates this condition should clearly indicate
* this fact. The recommended language is "Note: this comparator
* imposes orderings that are inconsistent with equals."
*/
@Override
public int compare(Object o1, Object o2) {
Person obj1 = (Person)o1;
Person obj2 = (Person)o2;
int result = 0;
if(obj1.getAge() > obj2.getAge()) {
result = 1;
} else {
result = -1;
}
if(obj1.getAge() == obj2.getAge()) {
if(obj1.getHeight() > obj2.getHeight()){
result = 1;
}
}
return result;
}
}
Comparator의 compare을 overriding 했을 때 위의 주석에 쓰여져 있는 부분을 보면 설명이 있음..
* The implementor must ensure that {@link Integer#signum
* signum}{@code (compare(x, y)) == -signum(compare(y, x))} for
* all {@code x} and {@code y}. (This implies that {@code
* compare(x, y)} must throw an exception if and only if {@code
* compare(y, x)} throws an exception.)<p>
* <p>
* The implementor must also ensure that the relation is transitive:
* {@code ((compare(x, y)>0) && (compare(y, z)>0))} implies
* {@code compare(x, z)>0}.<p>
* <p>
* Finally, the implementor must ensure that {@code compare(x,
* y)==0} implies that {@code signum(compare(x,
* z))==signum(compare(y, z))} for all {@code z}.
인터페이스를 구현한 클래스에서는 compare(x, y) 가 -1 이면 compare(y, z) 가 1 이어야 된다. -> 대칭성
인터페이스를 구현한 클래스에서는 추이성을 보장해야 한다. (transitivity) -> 추이성
인터페이스를 구현한 클래스에서는 compare(x, y) ==0 이면 compare(x, z) == compare(y, z) 어야 한다.
TimSort의 정렬 알고리즘을 사용할 때 중요한 것
1. 반사성 : a와 a를비교할 때 항상 같아야 한다. ( a == a )
2. 대칭성: a > b 라면 b < a 가 되어야 한다.
3. 추이성: a < b 이고 b < c 이면 a < c 여야 한다.
4. 일관성 : 두 객체 a와 b가 같다면, 다른 비교가 반복되어도 계속 같아야 한다. 즉, compare(a,b)가 0 이라면 a와 b가 변하지 않는 한 compare(a,b)가 항상 0이어야 한다.
비교 연산에서 중요한 성질로 네 가지를 모두 만족해야 Comparator가 안전하고 일관되게 사용될 수 있다.
--> 위배되면 어쩔때는 정렬되고 어쩔때는 안되는 상황이 발생할 수 있다.
내가 정렬에 에러를 마주쳤을 때 아래 메소드를 통해서 정렬한 경우에는 정렬이 잘 되었었다.
firstFunction() {
let results = property(code) //[{name: "John", age: 23}] 가 있다고 가정
...
secondFunction(results);
}
secondFunction(records) {
...
thirdFunction(records);
}
thirdFunction(data) {
...
data.unshift({name: "Sandy", age: 25});
}
완전 같은 건 아니지만 이런 상황인 경우..
firstFunction안에서 secondFunction으로 넘겼던 results가 secondFunction을 거쳐 thirdFunction에서 파라미터로 받아져서 객체를 배열에 추가 시킨것... 즉 처음 가져왔었던 property(code)에서 가져온 원래 배열에도 추가가 된 것이다...
요약
객체는 참조에 의해 할당되고 복사됩니다. 변수엔 객쳊체가 아닌 메모리상의 주소인 참조가 저장됩니다. 따라서 객체가 할당된 변수를 복사하거나 함수의 인자로 넘길 땐 객체가 아닌 객체의 참조가 복사됩니다.
그리고 복사된 참조를 이용한 모든 작업(프로퍼티 추가.삭제 등)은 동일한 객체를 대상으로 이뤄집니다.
배열의 내부 동작 원리
배열은 특별한 종류의 객체입니다. 배열 arr의 요소를 arr[0]처럼 대괄호를 사용해 접근하는 방식은 객체 문법에서 왔습니다. 다만 배열은 키가 숫자라는 점만 다릅니다.
숫자형 키를 사용함으로써 배열은 객체 기본 기능 이외에도 순서가 있는 컬렉션을 제어하게 해주는 특별한 메서드를 제공합니다. length라는 프로퍼티도 제공하죠. 그렇지만 어쨌든 배열의 본질은 객체입니다.
이렇게 배열은 자바스크립트의 일곱 가지 원시 자료형에 해당하지 않고, 원시 자료형이 아닌 객체형에 속하기 때문에 객체처럼 동작합니다.
import kotlin.math.PI
//Functions 함수
//함수 선언할때는 fun keyword를 사용한다.
fun hello() {
return println("Hello, world!")
}
//x, y는 함수의 파라미터이고 Int타입. return타입 또한 Int타입.
/*fun sum(x: Int, y: Int): Int {
return x + y
}*/
//위의 함수를 Single-expression function으로 바꾸면
fun sum(x: Int, y: Int) = x + y
//이렇게 할 수 있음. 대괄호를 없애고 할당연산자 = 를 통해 함수몸체를 선언한다. 코틀린의 type inference덕분에 return type또한 제거할 수 있다.
//Named arguments, Default parameter values
//파라미터에 이름을 붙이면 코드의 가독성이 좋아짐
fun printMessageWithPrefix(message: String, prefix: String = "Info") {
println("[$prefix] $message")
}
//Return이 없는 함수
fun printMessage(message: String) {
println(message)
// `return Unit` or `reutnr` is optional
//함수에서 return value가 없으면 그 함수의 return type은 Unit이다. Unit은 오직 하나의 value를 갖는 type이다.
//함수 몸체 안에 명시적으로 선언할 필요가 없다.
}
fun main() {
hello()
println(sum(1, 2))
printMessageWithPrefix("Hello, world!", "Greeting")
//named arguments를 통해 직접 할당하게 되면 파라미터의 순서를 바꿀 수 있다.
printMessageWithPrefix(prefix = "Log", message = "Hello")
//prefix는 default value를 주어서 파라미터를 안넘기면 기본값이 출력된다.
printMessageWithPrefix("This is a custom message.")
//[Info] This is a custom message.
//return이 없는 함수
printMessage("This function doesn't have return type.")
}
fun circleArea(radius: Int) = radius * radius * PI
//Control flow
//conditional expreesions
//kotlin은 if 와 when을 조건표현으로 제공한다.
fun main() {
//if를 사용하면 소괄호()안에 조건을 주고 tru일 경우 {}안에 코드를 실행한다.
val d: Int
val check = true
if (check) {
d = 1
} else {
d = 2
}
println(d)
//코틀린에는 3항 연산자는 없다.
//대신 if를 {}없이 사용할 수 있다.
val a = 1
val b = 2
println(if (a > b) a else b)
//when은 조건절인데 여러 개의 조건을 비교할 수 있다. 약간 swich같은 느낌
val obj = "Hello"
when (obj) {
"1" -> println("One")
"Hello" -> println("Greeting")
//"Hello" -> println("Greeting2") // 이건 실행 안됨
else -> println("Unknown")
}
//만족하는 분기문을 순차적으로 실행하므로 만족한 1개만 실행된다는 것에 유의.
//when을 이용했을 때 변수에 바로 할당된다.
val result = when (obj) {
"1" -> "One"
"Hello" -> "Greeting"
else -> "Unknown"
}
println(result)
//Boolean절을 여러 개 체크 하는 경우에도 유용하다.
val temp = 18
val description = when {
temp < 0 -> "very cold"
temp < 10 -> "a bit cold"
temp < 20 -> "warm"
else -> "hot"
}
println(description)
//Ranges
//가장 일반적으로 범위를 만드는 경우에 ..operator를 사용한다.
//1..4 는 1,2,3,4와 같다.
//마지막을 포함하지 않게 선언하고 싶으면 ..<를 사용한다.
//1..<4 는 1,2,3과 같다.
//역순으로 선언할 때는 downTo를 사용한다.
//4 downTo 1은 4,3,2,1과 같다.
//1이 아닌 증가량의 범위를 선언하고 싶으면 step을 사용한다.
//1..5 step 2는 1,3,5와 같음
//Char에서도 사용 가능하다.
//'a'..'d'는 'a','b','c','d'와 같다.
//'z' downTo 's' step 2는 'z', 'x', 'v', 't'이다.
//Loops
//가장 흔한 loop structures로 for과 while이 있음.
//for은 반복하면서 action을 수행하는 것이고,
//while은 특정한 조건이 만족될때까지 action을 수행함.
//for
//for loop를 통해 정해진 범위를 반복할 수 있음.
for (number in 1..5) {
print(number)
}
println()
//Collection에서도 loop를 통해 반복될 수 있다.
val cakes = listOf("carrot", "cheese", "chocolate")
for (cake in cakes) {
println("Yummy, it's a $cake cake!")
}
//while은 두 방법으로 사용될 수 있다.
//1. 조건절이 참일 경우 실행: while
//2. 코드블록을 먼저 실행하고 그 다음에 조건절을 체크한다.: do-while
var cakesEaten = 0
var cakesBaked = 0
while (cakesEaten < 3) {
println("Eat a cake")
cakesEaten++
}
do {
println("Bakes a cake")
cakesBaked++
} while (cakesBaked < cakesEaten)
}
fun main() {
for(number in 1..100){
val result = when {
number % 3 == 0 && number % 5 == 0 -> "fizzbuzz"
number % 3 == 0 -> "fizz"
number % 5 == 0 -> "buzz"
else -> number
}
println(result)
}
}
//변수는 main함수 바깥에서 선언될 수 있는데, 이런 경우에는 top level에 선언되었다고 한다.
val value = 10
//value = 20 //val로 선언했기 때문에 이 후에 재할당이 불가하다.
var variable = 20
//var variable2 //선언만 하면 안되고 초기화까지해야한다.
//fun 으로 function을 선언한다.
fun main() {
println("Hello, world!")
println(value)
variable = 30
println(variable)
val customers = 10
//String templates
//그냥 해당 값만 출력할거면 $변수명 으로
println("There are $customers customers")
//값을 수정한다면 ${}를 사용한다.
println("There are ${customers + 1} customers")
}
//read-only variables with val 변하지않는 값
//mutable variables with var 할당 후 변할 수 있는 값을 담는 곳
//공식문서에서는 모든 변수를 val로 선언하고 할당 후 변할 수 있는 값은 var로 선언할 것을 추천함.
java.util.Hashtable는 동기화를 지원하고, 키, 값에 null을 넣을 수 없지만, java.util.HashMap은 동기화를 지원하지 않고 키, 값에 null을 넣을 수 있다.
순서가 없다.
기본 연산인 get, put은 해쉬함수를 통해 elements들을 buckets에 넣고 가져오고, 이러한 연산에는 일정한 성능을 보인다.
HashMap안에 있는 요소들을 보기위한 iteration 성능이 중요하다면 처음 capacity를 (the number of buckets) 너무 크거나 적게 주지 않는 것이 중요하다. -> HashMap 인스턴스의 capacity와 key-value 매핑된 수의 크기의 비례하는 시간을 필요로 하므로.
HashMap은 2개 파라미터를 인스턴스로 갖는데, initial capacity와 load factor은 성능에 영향을 미친다. capacity는 hash table이라는 자료 구조에서 쓰이는 buckets의 수를 말하고, 해당 파라미터의 initial capacity는 hash table이 생성될 시의 capacity를 말함.또 다른 파라미터 load factor은 capacity가 자동으로 증가되기 전에 hash table이 얼마나 차있는지 측정한다. load factor와 현재 capacity의 곱의 수가 hash table을 초과하면, rehash되고(내부 데이터구조가 다시 재정립되는 것), hash table은 대략 2배의 buckets을 갖게된다.
일반적으로 기본 load factor(.75)은 시간과 공간비용 측면에서 적절한 밸런스를 제공한다. 더 큰 값은 공간 오버해드는 줄여주지만 찾는 비용이 늘어난다. (get, put을 포함한 대부분의 operation에 영향을 끼침.)
따라서 2개의 인스턴스인 initial capacity와 load factor의 값 설정시 rehash operation을 최소화 하기 위해 고려해야한다. load factor로 개체의 최대 개수(데이터 입력할 최대개수)를 나눴을 때 initial capacity가 더 크면 rehash는 일어나지 않는다.-> HashMap의 인스턴스에 많이 저장되야 되면 처음 생성할 때 부터 init capacity를 크게 주면 rehash하지 않아도 되니까 더 효율적일 수 있다.
많은 키를 사용할 때 같은 hashCode()를 사용하는 것은 성능에 좋지 않다. 영향을 개선시키기 위해 키가 Comparable클래스 일 때 comparison order를 사용하면 키들 간의 연결을 끊을 수 있다. // 이 부분은 무슨 말을 하고 싶은지 몰겠음.
동기화를 지원하지 않는 클래스이다. Collenctions.synchronizedMap을 통해 감싸(wrapped)져서 동기화(멀티쓰레드 환경)에 안전하게 사용할 수 있음.
Map m = Collections.synchronizedMap(new HashMap(...));
만약 다른 쓰레드에서 바꾸면 ConcurrentModificationException을 날림.