반응형

 

싱글톤 패턴은 소프트웨어 디자인 패턴중 하나이다.

 

주로 사용되는 경우는 단 하나만 존재해야되는 물건이 있을 때!

프로그램 내부에서 단 1개만 생성됨을 보장하며 멀티쓰레드에서 이 객체를 동시에 접근하는 경우에 발생하는 동시성 문제를 해결해준다.

 

말로설명하면 어렵고 코드를 보면 쉽다.

class Order {
    public void start() {
        A a = A.getInstance();
        A b = A.getInstance();
        // 확인
        System.out.println(a == b); // true
    }
}

 

만약 이렇게 있다고 치면, a 와 b 는 같은 객체를 보고있게 된다.

이런 로직을 만들 필요가 있을때 싱글톤패턴을 사용한다.

 


1. 기본적인 구현방법 (리소스낭비)

public class A {

	private A() {
           System.out.println("Hello singleton!!");
	}

	private static A singleton = new A(); //객체생성

	public static A getInstance() {
            return A;
	}

}

 

private 생성자를 사용하여 외부에서의 생성을 막은것이 포인트다

단점으로는 클래스가 로드될때 객체가 생성되므로 리소스 낭비가 있을 수 있다.

 

그렇다면 어떻게 개선할수있을까?

 

2. 기본적인 구현방법 (리소스낭비 줄임)

public class A {
    // 단 1개만 존재해야 하므로 static
    private static A a;

    // private로 외부생성을 막음
    private A() {
       System.out.println("Hello singleton!!");
    }

    //외부에서는 getInstance로 접근
    public static A getInstance() {
      if (instance == null){
          instance = new A();
      }
      return instance; 
    } 
}

 

1) getInstance()

 - static을 쓰지않으면 외부에서 사용할때

new A().new getInstance()

이렇게 새로운 인스턴스를 선언하면서 써야하기때문에 싱글톤이 보장되지 않는다.

따라서 static을 사용하고 if 문을 통하여 기존에 불려진 instance가 없다면 생성, 있다면 기존 instance를 return

 

하지만 단점!

이 경우 Multi Thread 환경에서 Thread-safe 를 보장해주지 않는다.

n개의 사용자가 동시에 getInstance를 했을때 new A() 를 n번 생성할수도있다.

 

이를 막기위해서 Synchronized, Double check 등을 사용해서 막아볼수도 있겠지만, 가장많이 쓰이는 방법은 아래와 같다.

 

3. LazyHolder 싱글톤 패턴 ✔️

public class A {

    private A() {
       System.out.println("Hello singleton!!");
    }
    
    private static class LazyHolder() {
        private static final A instance = new A();
    }

    public static A getInstance() {
        return LazyHolder.instance;
    }
    
}

 

 

훨씬 코드가 예뻐진것을 볼수있다.

 

해당 클래스가 로드되더라도 getnstance() 메서드가 호출될때까지 로드되지 않으므로 리소스 낭비도 줄이고

 

A.getInstance() 를 호출할때마다 동일한 instance가 변환된다. 왜냐! private static final 로 선언했기때문!

 

사용은 아래와 같이 쓰면된다

public static void main(String[] args) {
	A a = A.getInstance();
}

Spring 의 경우에는 static 기반의 싱글톤 구현을 지양하고있으며 더욱 간단하게 쓸수있다.

 

Spring - @Autowired 사용

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class A {
    public A() {
        System.out.println("Hello singleton!!");
    }
}

@Component
public class Main {
    @Autowired
    private A a; // A의 싱글톤 인스턴스가 주입됨

    public void display() {
        System.out.println(a);
    }
}

// 메인 메서드
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Main main = context.getBean(Main.class);
        main.display();

        // 다시 가져와서 같은 인스턴스인지 확인
        Main anotherMain = context.getBean(Main.class);
        System.out.println(main.a == anotherMain.a); // true, 같은 인스턴스
    }
}

 

위와 같이 인스턴스 필드로 의존성 주입을 사용하는 경우, 싱글톤 패턴이 유지된다.

Spring 컨테이너는 @Component, @Service, @Repository, @Controller 등으로 정의된 클래스의 인스턴스를 싱글톤으로  관리한다

 

항상 어노테이션을 사용하여 써왔기때문에 싱글톤 패턴을 정석으로 봤을때 생소했던것같다.

728x90

'CS' 카테고리의 다른 글

종속성, 의존성  (0) 2024.10.31
반응형

 

항상 사용하고있던 종속성, 의존성에 대한 고찰.

 

종속성

: 한 클래스가 다른 클래스나 기능을 필요로 할때 발생하는 관계.

예를들어 class B 가 class A 의 변수 혹은 함수에 의존한다면, class B는 class A의 종속성을 가진다고 할 수 있다.

 

의존성

: 종속성을 관리하는 방식.

흔히 알고있는 DI (Dependency Injection) 의존성 주입 이라고한다.

 

예를들어 class B에서 class A를 선언하지않고, 외부에서 class A의 인스턴스를 주입받으면 class A의 속성이나 인스턴스 그대로 사용가능하다.

 

이것을 왜 헷깔리고 자주 잊는가?

나는 개인적으로 외부 class를 선언하고 사용하는것에 너무 익숙해져있기 때문이다.

 

그러한 행위가 가능한 이유는 전부 DI 덕분이라는것을 다시 생각해보았다. 

728x90

'CS' 카테고리의 다른 글

싱글톤 패턴의 이해와 응용  (0) 2024.10.31
반응형

작업은 vscode 에서 진행한다.

만약 없다면 무료 툴 이므로 다운로드!

 

1. node js 설치

https://nodejs.org/en

 

 

2.  cordova 설치

cmd 창에서 진행하면 된다

$ npm install -g cordova

 

3. cordova 프로젝트 생성

내가원하는 폴더로 이동 후 아래 커맨드 실행

$ cordova create [프로젝트명] [패키지명] [프로젝트명]

ex)
$ cordova create cordova1 com.example.test cordova1

 

이렇게 프로젝트가 생성된다

 

처음에는 기본적으로 www 즉 웹만 생성되며 실행시켜볼수도있다.

 

vscode 플러그인으로 live server 를 추가하고 (구글링) 실행하면 코르도바 이미지가 보인다

 

 

 

4.  Android, iOS platform 추가

아래 커맨드로 안드로이드 소스코드를 추가할 수 있다

$ cordova platform add android

 

마찬가지로 ios로 추가해주면 아래와같이 platoforms에 android 와 ios가 생긴다

 

Android 같은 경우는, java 기반으로 소스코드가 생기는것을 확인해 볼 수 있다.

 

5. project build

 

이제 이것을 vscode에서 그대로 build 하려면 ios의 경우는 맥북이 필요하고

android 는 build 가능하나, 필요한 requirements들이 있다 (jdk 등등) 대신 안드로이드 스튜디오에서 해당프로젝트 실행해도됨

 

$ cordova requirements

 

나의 경우 위에서부터 하나씩 보면 

jdk : 17버전 있음

sdk  : 있음

android target : 없음

gradle : 없음

macOS : 없음

 

이렇게 확인된다

 

특히 android target 부분이나 밑에 gradle 을 보면 please install android studio 라는 부분을 볼수있는데 안드로이드 스튜디오에서 해당 프로젝트를 빌드하는게 속편할것같다

728x90
반응형

 

Example 4 : s = "{()}" output = true;

 

예시를 보면 소/중/대괄호가 짝지어서 나오고 Example 4 와 같이 속에있을수도있다.

 

처음에는 String 의 Char.At(i) 가 ( 혹은 { 혹은 [ 일때 Char.At(i +1) 이 ) 혹은 } 혹은 ] 인것을 확인했는데

example 4 때문에 그건 불가능했다.

 

해당문제는 여는 괄호가 나오면 그다음엔 무조건 닫는 괄호가 나와야하기때문에 여는 괄호가 나오면 stack 에 저장해두고, 더이상 여는 괄호가 없으면 stack 에서 가장 나중에 저장된 괄호를 지우는 방식으로 진행했다.

 

class Solution {
    public boolean isValid(String s) {
    	Stack<Character> stack = new Stack<>();
        Map<Character, Character> map = new HashMap<>();
        map.put('(',')');
        map.put('{','}');
        map.put('[',']');
    }
}

 

여는괄호인지를 알기위해서 map을 만들어줬다.

class Solution {
    public boolean isValid(String s) {
    	Stack<Character> stack = new Stack<>();
        Map<Character, Character> map = new HashMap<>();
        map.put('(',')');
        map.put('{','}');
        map.put('[',']');
        
        for (int i=0; i<s.length(); i++) {
        	char c = s.charAt(i);
            
            if (map.containsKey(c)) { //여는 괄호가 나왔다면
            	stack.push(c);
            } else {
            	if (c != map.get(stack.pop()) {
                   return false;
                }
            }
        }
        
        return stack.length == 0;
    }
}

 

 여는 괄호가 나왔다면 stack에 넣어주고 

닫는괄호가 나왔다면, stack에 있는 여는괄호를 불러서 map.get 으로 나온 value와 비교하였다.

 

그리고 모든 닫는괄호가 정상적으로 짝지어나왔다면 stack.length 는 0일 것이므로 return 해줬다.

 

그런데 이렇게 하면 s="]" 일때 .. stack 에도 값이 없기때문에 else 로 빠지지만, 갈곳이없다..!

 

때문에 empty 도 같이 봐준다.

 

class Solution {
    public boolean isValid(String s) {
    	Stack<Character> stack = new Stack<>();
        Map<Character, Character> map = new HashMap<>();
        map.put('(',')');
        map.put('{','}');
        map.put('[',']');
        
        for (int i=0; i<s.length(); i++) {
        	char c = s.charAt(i);
            
            if (map.containsKey(c)) { //여는 괄호가 나왔다면
            	stack.push(c);
            } else {
            	if (stack.isEmpty() || c != map.get(stack.pop())) {
                   return false;
                }
            }
        }
        
        return stack.size() == 0;
    }
}

 

 

 

728x90
반응형

 

 

딱 봤을때 String 으로 만들어서 뒤집어야겠다는 생각을 했다.

 

import java.util.*;

class Solution {
    public long solution(long n) {
        
        //String 배열로 만듦
        String[] arr = String.valueOf(n).split("");
        
        //순서 뒤집음
        Arrays.sort(arr, Comparator.reverseOrder());
        
        //하나의 String으로 변환
        String str = "";
        for (String s : arr) {
            str += String.valueOf(s);
        }
        
        //long 변환
        long answer = Long.parseLong(str);
        return answer;
    }
}

 

그런데 String 으로 변환하는 부분이 마음에 들지않는다.

좀더 간단한 방법이 있을것같고, 내림차순으로 하기위해서 Coparator.reverseOrder() 라는 함수가 있다는것을 외워야한다.

 

다른 사람의 풀이를 살펴보았다.

 

import java.util.*;

class Solution {
    public long solution(long n) {
        
        //String 배열로 만듦
        String[] arr = String.valueOf(n).split("");
        
        //순서 뒤집음
        Arrays.sort(arr);
        
        //하나의 String으로 변환
        StringBuilder sb = new StringBuilder();
        for (String s : arr) sb.append(s);
        
        //long 변환
        long answer = Long.parseLong(sb.reverse().toString());
        return answer;
    }
}

 

StringBuilder 를 사용하면 Comparator.reverseOrder() 이라는 다소 긴 메소드를 기억할 필요없이

new StringBuilder 선언하고 append 로 담은다음에 sb.reverse() 만 해주면 된다.

 

728x90
반응형

public int solution(int n) {
    int answer = 0;

    String str = Integer.toString(n);
    char[] charArr = str.toCharArray();

    for (char c : charArr) {
        int num = Character.getNumericValue(c);
        answer += num;
    }

    return answer;
}

 

n 을 string으로 바꾸고 하나씩 char 로 떼서 형변환 후 더해줬다.

 

이렇게하면 코드가 쓸데없이 길어지므로 String.valueOf()를 사용해서 split 시켜보자.

 

Integer.toString() 도 있지만, 만약 변수가 null 이라면 nullPointException 발생하므로 가급적 String.valueOf() 를 쓰도록 한다.

 

public int solution(int n) {
    int answer = 0;

    String[] str = String.valueOf(n).split("");

    for (String s : str) {
        answer += Integer.parseInt(s);
    }

    return answer;
}

 

이렇게 깔끔하게 변신~

728x90
반응형

간단한 Lv.1

 

x만큼 배열을 늘리면된다.

 

class Solution {
    public long[] solution(int x, int n) {
        long[] answer = new long[n];
        
        return answer;
    }
}

 

우선 array 길이설정을 해주고

 

x를 기존 값에 더해나가고싶다.

class Solution {
    public long[] solution(int x, int n) {
        long[] answer = new long[n];
        
        for (int i=0; i<n; i++) {
           answer[i] = answer[i-1] + x;
        }
        return answer;
    }
}

 

그런데 이렇게하면 첫번째 루프에서는 answer[0] 의 -1 값은 없으므로  0만 따로 설정해준다.

 

class Solution {
    public long[] solution(int x, int n) {
        long[] answer = new long[n];
        answer[0] = x;
        for (int i=1; i<n; i++) {
           answer[i] = answer[i-1] + x;
        }
        return answer;
    }
}

 

728x90
반응형

 

 

start 지점까지 가서 뭔가를 저장하고있다가 뒤집어야겠다는 생각을 했었는데,

그러면 시간이 너무오래걸릴것같았다.

 

책의 풀이를 참고해보니 일단 start 지점까지 가는것으로 시작하였다.

 

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
    
    if (head == null) return head;
    
    ListNode root = new ListNode(0);
    root.next = head;
    
    ListNode start = head;
    
    if (int i=0; i<left-1; i++ {
    	start=start.next;
    }
    
    }
}

 

우선 null 예외처리를 해주고

 

이리저리 손질보고나서 리턴을 예쁘게 하기위해 root 라는 새로운 ListNode 를 만들어주고 head 를 연결했다.

나중에 리턴할때는 root.next로 리턴할것이다.

 

앞으로 보낼 ListNode start 를 하나 만들고 left 파라미터 만큼 이동하기로했다.

주어진숫자는 자릿수이기때문에 index에 맞추려고 -1 해줌.

 

그리고 어떤식으로 살펴갈까 생각을 했는데

 

앞에 있는걸 뒤로 옮기자니 도루묵이 되어버렸고

start 지점을 기억하고 뒤에서 ++ 한 자릿수만큼 이동한뒤 끌어와야겠다고 생각했다.

 

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
    
    if (head == null) return head;
    
    ListNode root = new ListNode(0);
    root.next = head;
    
    ListNode start = root;
    
    for (int i=0; i<left-1; i++) {
    	start=start.next;
    }
    
    ListNode end = start.next;
    
    for (int i=0; i<right-left; i++) {
    	ListNode tmp = start.next; //start의 next 따로저장
        start.next = end.next; //start next.next를 next로 땡겨옴
        end.next = end.next.next; //end의 자릿수를 뒤로 이동 
        start.next.next = tmp; //다시 원래의 next를 저장
    }
    return root.next;
    
    }
}

 

end 를 만들어주고 loof를 돌렸다.

 

첫번째 loop

head = 1 2 3 4 5 6

start =  1 2 3 4 5 6

end = 2 3 4 5 6

ListNode tmp = start.next;
start.next = end.next;

 

tmp = 2 3 4 5 6

start = 1  + 3 4 5 6

start 의 next 가 end 의 next로 당겨졌다.

 

start 는 head 이므로, head 도 변하였다.

head = 1 3 4 5 6

end.next = end.next.next;
start.next.next = tmp;

 

 

end = 2 4 5 6 

 

end 의 next가 next.next로 당겨지고 이때 tmp 도 3이 빠진 2 4 5 6 으로 변하는 것으로..이해했다..

 

start.next.next = tmp 를 통해서 start = 1 3 2 4 5 6

 

두번째 loop

루프돌기전에

start = 1 3 2 4 5 6

end = 2 4 5 6

ListNode tmp = start.next;
start.next = end.next;

tmp = 3 2 4 5 6

 

start = 1 + 4 5 6

end.next = end.next.next;
start.next.next = tmp;

 

end = 2 5 6

이때tmp도 3 2 5 6

 

start = 1 4 3 2 5 6

 

..

이런식으로 바꿔나가면 된다!

728x90
반응형

 

ListNode 에서 홀수 index, 짝수 index를 뽑아서 짝수 index는 순서대로 뒤로 나열하는 문제다.

 

우선 홀짝을 순서대로 뽑아보자

class Solution {
    public ListNode oddEvenList(ListNode head) {
        ListNode odd = head;
        ListNode even = head.next;
    }
}

 

이렇게 초기화를 한 상태에서 head.next가 없을때까지 돌려야할것같은데..

우선 홀-짝-홀-짝 이런순서로 가니까 무조건 짝이 뒤에올것이기때문에 while문에 조건을 넣어줬다.

 

class Solution {
    public ListNode oddEvenList(ListNode head) {
        ListNode odd = head;
        ListNode even = head.next;
        
        while (even != null) {
            odd = odd.next.next;
            even = even.next.next;
            
            odd = odd.next;
            even = even.next;
        }
    }
}

 

이렇게되면 even에는 .next.next를 넣었으니, even= even.next 를 실행했을때 next값이 null 이라면 while문은 돌지않을것이다.

그런데 

 

1 -> 2 -> 3 -> 4 -> 5

 

예시가 있다고 하자.

 

while 문 돌기전에 odd = 1, even = 2

 

첫번째 while 문에서

odd = 3, odd.next = 5

even = 4, even.next = null 이다.

 

두번째 while문에서 odd = 5, even = null 이기때문에 nullpointException 발생한다.

따라서 while문을 수정해주자.

class Solution {
    public ListNode oddEvenList(ListNode head) {
        ListNode odd = head;
        ListNode even = head.next;
        
        while (even != null && even.next != null) {
            odd = odd.next.next;
            even = even.next.next;
            
            odd = odd.next;
            even = even.next;
        }
    }
}

이렇게하면 비록 두번째 루프는 돌지않았지만, odd.next를 설정했으므로 odd의 세팅이 완료된다.

 

그리고 최종적으로 기존의 odd 뒤에 even을 붙여준다.

class Solution {
    public ListNode oddEvenList(ListNode head) {
        ListNode odd = head;
        ListNode even = head.next;
        
        while (even != null && even.next != null) {
            odd = odd.next.next;
            even = even.next.next;
            
            odd = odd.next;
            even = even.next;
        }
        odd.next = even;
        
        return head;
    }
}

 

그리고 최종적으로 return 은 head를 해준다.

왜냐하면 odd를 return 하면 odd의 시작점을 알수없기때문~

 

마지막으로 null 예외케이스를 넣어주면

class Solution {
    public ListNode oddEvenList(ListNode head) {
    
    	if (head == null) return null;
        
        ListNode odd = head;
        ListNode even = head.next;
        
        while (even != null && even.next != null) {
            odd = odd.next.next;
            even = even.next.next;
            
            odd = odd.next;
            even = even.next;
        }
        odd.next = even;
        
        return head;
    }
}

 

728x90
반응형

 

딱 보면 홀수/짝수로도 생각할수있겠지만, 다음걸 가져와서 현재랑 swap 해주는 느낌으로 해보자

 

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode node = head;
    }
}

 

연산 용 node 를 하나 선언해준다.

 

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode node = head;
        
        while (node != null && node.next != null) {
            int temp = node.val;
            node.val = node.next.val;
            node.next.val = temp;
            node = node.next.next;
        }
        return head;
    }
}

 

temp에 현재 node.val 을 넣어두고

현재 node.val = next 값으로 넣어주고

next 값은 temp를 넣어줌!!

 

그리고 2개의 쌍을 바꿨으니 node 위치를 .next.next로 보내준다.

 

리턴할때는 head를 리턴하는데, node의 포인트는 이미 node의 끝을 향하고있기때문에 head를 보내준다

728x90

'BackEnd > 알고리즘' 카테고리의 다른 글

leetcode 92. Reverse Linked List II  (0) 2024.08.03
leetcode 328. Odd Even Linked List  (0) 2024.08.03
leetcode 2. Add Two Numbers  (0) 2024.07.31
leetcode 206. Reverse Linked List  (0) 2024.07.31
leetcode 21. Merge Two Sorted Lists  (0) 2024.07.31

+ Recent posts