반응형

안녕하세요.

git을 사용하면서 제외해야 되는 파일이 있습니다.

그래서 gitignore 파일에 제외해야 될 파일을 작성을 했습니다.

git add 그리고 git commit을 했지만 적용이 안 되는 현상이 발생했습니다.

이럴 때 < git rm -r --cached . > 명령어를 통해서 캐시를 삭제하고 다시 add / commit을 해주시면 되겠습니다.

git rm -r --cached .
git add .
git commit -m 'clear git cache'
git push

 

위의 사진은 실제 위의 명령어를 통해서 gitignore 등록한 파일이 제외된 모습입니다.

도움이 되셨으면 좋겠습니다.

감사합니다.

반응형
반응형

배열에 값을 넣는 방법은 알겠는데 프로그래밍을 하다 보면 배열의 정렬이 때때로 필요로 합니다.

또한 타입이 객체 타입인 경우에도 원하는 기준대로 정렬을 할 수 있도록 알아보도록 하겠습니다.

 

1. 가장 기본적인 원시 타입(int형)인 경우 배열의 오름차순 정렬을 보겠습니다.

 

int[] arr1 = {11, 5, 30, 40, 35, 80, 60};
Arrays.sort(arr1);
for(int i : arr1) {
	System.out.println(i);
}

간단합니다. Arrays.sort()함수에 배열을 넘겨주시면 됩니다.

 

arr1의 배열의 요소가 오름차순으로 정렬된 모습을 보실 수 있습니다.

sort는 기본 정렬 조건이 오름차순으로 되어 있기 때문에 특별한 코드 없이 오름차순으로 정렬이 됩니다.

 

 

2. 내림차순 정렬을 알아보겠습니다.

 

Integer[] arr2 = {11, 5, 30, 40, 35, 80, 60};
// Collections 메소드 사용
Arrays.sort(arr2, Collections.reverseOrder());

// 직접 구현도 가능
Arrays.sort(arr2, new Comparator<Integer>() {
	@Override
	public int compare(Integer i1, Integer i2) {
		return i2 - i1;
	}
});

for(int i : arr2) {
	System.out.println(i);
}

내림차순 정렬방법도 오름차순과 동일하게 진행됩니다. 하지만 인자로 내림차순이 될 수 있도록 구현된 Comparator를 넘겨 주셔야 됩니다. (Comparator가 무엇인지는 잠시 후에 살펴보겠습니다.) Comparator를 구할 때는 Collections메서드를 사용하시거나 직접 구현을 해주시면 되겠습니다.

 

 

 

3. Comparable 와 Comparator는 무엇일까?

 

위의 내용으로 배열의 정렬은 하실 수 있습니다. 이제는 정렬을 조금 더 잘 사용할 수 있도록 Comparable과 Comparator를 알아보도록 하겠습니다. 이 둘을 배우고 나면 객체도 정렬을 하실 수 있겠습니다.

Comparable과 Comparator는 정렬을 위한 인터페이스입니다. 각각의 인터페이스를 구현해서 "구현한 로직대로 정렬을 하겠다"입니다. 위에서 Arrays.sort() 함수를 통해서 정렬을 할 수 있었던 이유는 이미 Comparator가 내부적으로 구현이 되어 있기 때문입니다. 이렇게만 보면 이해가 잘 안 갑니다. 아래의 내용을 살펴보겠습니다.

 

 

4. Comparable

 

Comparable 인터페이스에는 compareTo() 추상 메소드 하나만 존재합니다.

주어진 객체(T o) 보다 작으면 음수, 같으면 0, 크면 양수를 리턴합니다.

이게 무슨말인지 소스 코드를 보겠습니다.

public class NameCard implements Comparable<NameCard> {
	public String name;
	public int age;
	
	public NameCard(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
	public int compareTo(NameCard o) {
		if(this.age < o.age) {
			return -1;
		} else if(this.age == o.age) {
			return 0;
		} else {
			return 1;
		}
	}
}

NameCard라는 클래스에 Comparable를 구현하였습니다. NameCard를 컬랙션에 줄줄이 넣고 정렬을 한다면 어떻게 하시겠습니까? 정렬의 기준은 어떻게 될까요? 정렬의 기준을 이름으로 할까요? 아니면 나이가 될까요? 정렬의 기준이 없습니다. 그렇기 때문에 정렬의 기준을 정하기 위해서 Comparable를 구현하는겁니다. 위의 코드를 볼 때 정렬의 기준은 나이(age)가 되겠습니다. 테스트를 하도록 하겠습니다.

 

List<NameCard> nc = new ArrayList<>();
nc.add(new NameCard("자바", 8));
nc.add(new NameCard("파이썬", 7));
nc.add(new NameCard("장고", 15));
nc.add(new NameCard("스프링", 10));

Collections.sort(nc);
for (NameCard obj : nc) {
	System.out.println(obj.name +" "+obj.age);
}

NameCard 객체를 리스트에 담아주세요. 그리고 Collections.sort() 함수를 통해서 정렬을 해주시면 되겠습니다.

 

오름차순으로 이상 없이 정렬이 된 모습을 보실 수 있습니다. 만약 내림차순을 원하시다면 compareTo 로직을 반대로 변경해주시면 됩니다.

 

 

5. Comparator

 

comparable 정렬이 일반적이고 객체에 이미 정해진 정렬이라면, Comparator는 조금 더 복잡하게 정렬도 가능하고 필요할 때마다 구현해서 사용할 수 있는 인터페이스입니다.

Comparator 인터페이스는 int compare(T t1, T t2)를 구현해주셔야 됩니다.

compareTo와 마찮가지로 t1과 t2가 같다면 0, t1이 t2 보다 앞에 오게 하려면 음수, t1이 뒤쪽으로 오게 하려면 양수를 반환하시면 됩니다.

List<NameCard> nc = new ArrayList<>();
nc.add(new NameCard("자바", 8));
nc.add(new NameCard("파이썬", 7));
nc.add(new NameCard("장고", 15));
nc.add(new NameCard("스프링", 10));

Comparator<NameCard> cp = new Comparator<>() {
	@Override
	public int compare(NameCard n1, NameCard n2) {
		return n2.age - n1.age;
	}
};

Collections.sort(nc, cp);
for (NameCard obj : nc) {
	System.out.println(obj.name +" "+obj.age);
}

코드가 조금 다른 점은 Comparator를 구현하고 sort()함수에 인자로 같이 넘겨줬습니다.

익명 객체라 하여 이름 있는 객체를 만드는 것이 아닌, 객체를 바로 생성하였습니다. 그리고 sort() 함수에 전달을 했는데요. 기존에 NameCard에 comparable이 구현이 되어 있음에도 불구하고 Comparator이 적용된 모습을 보실 수 있습니다.

 

 

Comparator 인터페이스는 코드 작성 중 정렬이 필요할 때마다 위와 같이 익명 객체로 구현해서 적용을 해주시면 더 편리하게 정렬이 가능하겠습니다.

 

감사합니다.

반응형
반응형

List와 같은 컬랙션에서 요소들을 순차적으로 처리하기 위해서 iterator 반복자가 존재합니다.

예제를 보고 간단히 살펴보겠습니다.

public class IteratorExample {
	public static void main(String[] args) {
		List<String> list = Arrays.asList("자바", "파이썬", "스프링", "장고");
		Iterator<String> iterator = list.iterator();
		while(iterator.hasNext()) {
			String name = iterator.next();
			System.out.println(name);
		}
		
		for (String s : list) {
			System.out.println(s);
		}
	}
}

자바 / 파이썬 / 스프링 / 장고 4개의 리스트 요소를 포함하고 있는 리스트를 생성했습니다.

그리고 iterator() 함수를 통해서 Iterator 반복자를 갖고 옵니다.

hasnext() 함수를 통해서 항목이 있는지 확인을 하고 next() 함수를 통해서 요소를 하나씩 하나씩 갖고 오면 되겠습니다.

아래의 for문은 iterator를 사용하지 않고도 for문을 통해서 요소들을 전체 출력을 할 수 있습니다.

 

 

다음으로는 iterator를 활용한 예제를 보도록 하겠습니다.

public class Iterators {

    public static class ListIterator<T> implements Iterator<T> {

        private final Iterator<Iterator<T>> listIterator;
        private Iterator<T> currentIterator;

        public ListIterator(List<Iterator<T>> iterators) {
            this.listIterator = iterators.iterator();
            this.currentIterator = listIterator.next();
        }

        @Override
        public boolean hasNext() {
            if(!currentIterator.hasNext()) {
                if (!listIterator.hasNext()) {
                    return false;
                }

                currentIterator = listIterator.next();
                hasNext();
            }

            return true;
        }

        @Override
        public T next() {
            hasNext();
            return currentIterator.next();
        }

        @Override
        public void remove() {
            hasNext();
            currentIterator.remove();
        }
    }

    public static <T> Iterator<T> singleIterator(final List<Iterator<T>> iteratorList) {
        return new ListIterator<>(iteratorList);
    }
}

1. singleIterator부터 살펴보겠습니다.

singleIterator는 정적 메서드이며 Iterator<T> 타입을 리턴합니다.

위의 ListIterator 클래스는 Iterator를 구현하였으니 Iterator 타입이겠죠?

singleIterator를 호출하면 ListIterator 객체가 리턴됩니다.

파라미터로는 Iterator<T>으로 구성된 리스트를 인자로 받습니다. 즉, 여러 (리스트로 만들어지고 난 후에) Iterator형의 요소들이 존재합니다.

 

2. ListIterator 클래스를 살펴보겠습니다.

ListIterator 생성자를 보면 singleIterator 메서드로부터 받은 Iterator요소가 담겨 있는 리스트를 넘겨받아서, 리스트의 Iterator를 저장합니다.

아하, 그러니까 리스트 안에는 Iterator타입의 요소들이 담겨줘 있고, 요소가 아닌 이 리스트의 Iterator를 다시 저장하는 것입니다.

currentIterator에는 첫 번째 요소를 저장합니다.

 

3. hasNext()를 살펴보겠습니다.

currentIterator(현재 이터레이터)의 다음 데이터가 없다면 listIterator.next()를 통해서 다음 요소를 갖고 옵니다.

listIterator.hasNext()를 호출할 때 리스트의 다음 요소가 없다면, 더 이상 요소가 존재하지 않습니다.

currentIterator 다음 데이터가 존재한다면 True가 리턴됩니다.

 

테스트 코드를 살펴보겠습니다.

public class IteratorsTest {

    @Test
    public void multipleIterators() {
        final Iterator<Integer> a = Arrays.asList(1, 2, 3, 4, 5).iterator();
        final Iterator<Integer> b = Arrays.asList(6).iterator();
        final Iterator<Integer> c = new ArrayList<Integer>().iterator();
        final Iterator<Integer> d = new ArrayList<Integer>().iterator();
        final Iterator<Integer> e = Arrays.asList(7, 8, 9).iterator();

        final Iterator<Integer> singleIterator = Iterators.singleIterator(Arrays.asList(a, b, c, d, e));

        assertTrue(singleIterator.hasNext());
        for (Integer i = 1; i < 10; i++) {
            assertEquals(i, singleIterator.next());
        }
        assertFalse(singleIterator.hasNext());
    }
}

a, b, c, d, e 각각의 Iterator 타입의 변수를 만들었습니다. 그리고 이 각각의 변수들을 List 요소에 넣고 singleIterator 메서드를 호출했습니다.

singleIterator 변수에 Iterator가 리턴이 되고, 이를 활용하여 각각의 요소들 별로 데이터를 추출하는 예제입니다.

즉, 하나의 Iterator 인터페이스로 여러 개의 Iterator를 다루는 예제입니다.

 

 

도움이 되셨으면 좋겠습니다.

감사합니다 :)

 

 

참고)

[예제] - JAVA 프로그래밍 면접 이렇게 준비한다

반응형
반응형

안녕하세요.

WeakReference에 대해서 간략하게 알아보고 예제를 살펴보도록 하겠습니다.

WeakReference는 클래스입니다 :) 그리고 GC(Garbage Collector)와 밀접한 연관성이 있습니다.

GC는 개발자의 영역이 아닌데 WeakReference을 사용함으로써 GC에게 어느정도 개입을 할 수가 있습니다.

GC는 불필요한 객체(참조하고 있지 않는)들을 힙 메모리에서 제거를 해줍니다. 특정 객체가 Weak Reference(약한 참조) 상태가 되면 GC는 이 객체를 제거를 합니다. 빠르게 객체를 제거함으로써 잠깐 사용하고 제거를 할 수 있는 것이죠.

new를 사용하여 객체를 만들때는 강한 Reference가 적용이 되는데, WeakReference를 사용하여 객체를 만들 때는 약한 Reference가 적용이 됩니다.

 

예제를 살펴보도록 하겠습니다.

    @Test
    public void weakReferenceStackManipulation() {
        final WeakReferenceStack<ValueContainer> stack = new WeakReferenceStack<>();

        final ValueContainer expected = new ValueContainer("Value for the stack");
        stack.push(new ValueContainer("Value for the stack"));

        ValueContainer peekedValue = stack.peek();
        assertEquals(expected, peekedValue);
        assertEquals(expected, stack.peek());
        peekedValue = null;
        System.gc();
        assertNull("Fail", stack.peek());
    }

위의 예제는 stack을 WeakReference형으로 선언하였습니다. WeakReference은 ValueContainer을 래퍼해줍니다.

ValueContainer 객체를 생성하고, 이 객체를 stack에 집어넣었습니다.

그리고 두 값을 비교하면 같은 값이 됩니다.

이때 peekedValue는 WeakReference 형으로 선언된 값을 참조하기 때문에, peekedValue 값에 null을 대입하면 WeakReference 형으로 선언된 객체는 GC가 동작될 때 제거됩니다. 그렇기 때문에 stack 값을 갖고 오면 stack에 값이 있을 것으로 생각이 되지만 GC에 의해서 이미 제거되어 null이 됩니다.

GC가 동작하지 않았더라면(System.gc() 주석 처리), GC가 동작하기 전까지는 null값이 아니기 때문에 마지막 테스트는 실패하게 됩니다.

LRU 캐시처럼 객체를 짧게 사용하는 곳에서는 WeakReference가 유용할 수 있겠습니다.

감사합니다.

반응형
반응형

안녕하세요.

JUnit의 생명주기, 즉 테스트가 수행되는 순서를 알아보겠습니다.

 

JUnit 순서

테스트가 수행될 때는 @Test 만 수행되는 것이 아닌 @BeforeClass, 생성자, @Before, @AfterClass 가 존재합니다.

생명주기를 이해하기 쉬운 방법 중 하나는 카운터를 세는 코드를 작성하시면 조금도 쉽게 이해할 수 있습니다.

일단 코드를 먼저 작성하겠습니다. (테스트 코드 기준은 JUnit4 입니다.)

public class JUnitLifecycle {
    private static int counter = 0;

    @BeforeClass
    public static void suiteSetup() {
        assertEquals(0, counter);
        counter++;
    }

    public JUnitLifecycle() {
        assertTrue(Arrays.asList(1, 5).contains(counter));
        counter++;
    }

    @Before
    public void prepareTest() {
        assertTrue(Arrays.asList(2, 6).contains(counter));
        counter++;
    }

    @Test
    public void peformFirstTest() {
        assertTrue(Arrays.asList(3, 7).contains(counter));
        counter++;
    }

    @Test
    public void performSecondTest() {
        assertTrue(Arrays.asList(3, 7).contains(counter));
        counter++;
    }

    @After
    public void cleanupTest() {
        assertTrue(Arrays.asList(4, 8).contains(counter));
        counter++;
    }

    @AfterClass
    public static void suiteFinished() {
        assertEquals(9, counter);
    }
}

1. @BeforeClass

테스트 클래스 시작 시 한번만 수행

-------------------------------------------------

2. 생성자

- 테스트 케이스 시작 전 호출

- (@Before/@After 이외에도 생성자도 호출이 되는 걸 인지해주세요)

 

3. @Before

- 테스트 케이스 시작 전 호출

- 테스트 값/상태 준비

 

4. @Test

- 테스트 케이스

- public이고 void 타입 값으로 반환

 

5. @After

테스트 케이스 종료 후 호출

- 테스트 후 정리

-------------------------------------------------

6. @AfterClass

모든 테스트 케이스 완료 후, 테스트 클래스 종료 전 수행

 

마지막 AfterClass 종료 시 카운터는 9인걸 보실 수 있습니다. 이유는 @Test(테스트 케이스)가 2번 호출이 되어 생성자와 @Before/@After 가 다시 한번 호출이 되었기 때문입니다.

 

핵심 메소드

assertEquals 			// 두개의 객체가 같은지 확인
assertTrue / asertFalse	// Boolean 결과값 확인
assertNotNull			// 객체가 Null이 아닌지 확인
assertArrayEquals		// 두 배열의 값이 같은지 확인

 

테스트 코드를 작성하기 위해서는 생각보다 많은 지식이 필요한 것은 아닙니다.

테스트 코드를 작성하기 어려운 프로젝트는 이미 개발이 많이 되어 있으며 모듈(함수/클래스) 단위가 커서 테스트 코드를 작성하기 어려운 소스코드가 존재할 때가 오히려 테스트 작성이 어렵습니다.

그렇기에 시작부터 프로그래밍을 하실 때는 항상 테스트코드를 생활화 하셔야겠습니다.

 

학습을 통해서 생명주기와, 핵심 메소드를 익혔습니다. 이제는 직접 테스트 코드를 작성하실 수 있겠습니다 :)

감사합니다.

반응형

+ Recent posts