# Set

#### Set의 개념

Dart에서 `Set`은 중복되지 않는 고유한 값들의 모음을 의미한다. 수학적인 집합과 유사하게, `Set` 내의 원소는 고유하며 중복을 허용하지 않는다. Dart의 `Set`은 리스트와 같은 컬렉션이지만, 주요 차이점은 중복된 요소를 허용하지 않는다는 점이다. 이는 특정한 요소가 이미 존재하는지 여부를 검사하거나, 중복을 제거할 때 유용하다.

#### Set의 생성과 기본 사용법

`Set`은 Dart에서 리스트와 유사하게 사용할 수 있으며, 다음과 같은 방법으로 생성할 수 있다:

```dart
Set<int> numbers = {1, 2, 3, 4, 5};
Set<String> names = {'Alice', 'Bob', 'Charlie'};
```

혹은 빈 `Set`을 생성한 후에 값을 추가할 수 있다:

```dart
Set<int> emptySet = {};
emptySet.add(10);
```

`Set`은 여러 타입의 값들을 저장할 수 있으며, Dart의 타입 시스템을 이용하여 제네릭 형태로 사용할 수 있다.

#### Set의 주요 연산

수학에서와 마찬가지로, Dart의 `Set`에서도 다양한 연산을 수행할 수 있다. 대표적인 연산으로는 합집합, 교집합, 차집합이 있으며, 이들 연산은 모두 `Set`에서 제공하는 메서드를 통해 수행할 수 있다.

**합집합 (Union)**

두 집합 $A$와 $B$의 합집합은 다음과 같이 정의된다:

$$
A \cup B = { x \mid x \in A \text{ 또는 } x \in B }
$$

Dart에서 합집합은 `union` 메서드를 사용하여 구할 수 있다:

```dart
Set<int> a = {1, 2, 3};
Set<int> b = {3, 4, 5};

Set<int> unionSet = a.union(b);
print(unionSet); // {1, 2, 3, 4, 5}
```

**교집합 (Intersection)**

두 집합 $A$와 $B$의 교집합은 다음과 같이 정의된다:

$$
A \cap B = { x \mid x \in A \text{ 그리고 } x \in B }
$$

교집합은 `intersection` 메서드를 사용하여 구할 수 있다:

```dart
Set<int> a = {1, 2, 3};
Set<int> b = {3, 4, 5};

Set<int> intersectionSet = a.intersection(b);
print(intersectionSet); // {3}
```

**차집합 (Difference)**

차집합은 두 집합 $A$와 $B$에서 $A$에만 존재하고 $B$에는 존재하지 않는 원소들로 구성된다:

$$
A - B = { x \mid x \in A \text{ 그리고 } x \notin B }
$$

Dart에서 차집합은 `difference` 메서드를 통해 구할 수 있다:

```dart
Set<int> a = {1, 2, 3};
Set<int> b = {3, 4, 5};

Set<int> differenceSet = a.difference(b);
print(differenceSet); // {1, 2}
```

**원소 포함 여부 확인 (Membership)**

집합에서 특정 원소가 포함되어 있는지 확인하는 연산은 매우 중요하다. Dart에서는 `contains` 메서드를 사용하여 특정 값이 집합에 포함되어 있는지 여부를 검사할 수 있다:

```dart
Set<int> numbers = {1, 2, 3, 4, 5};
print(numbers.contains(3)); // true
print(numbers.contains(6)); // false
```

#### Set의 반복 처리

`Set`은 Dart의 `for` 루프나 `forEach` 메서드를 통해 반복 처리할 수 있다. 이는 리스트와 유사하게 동작하지만, `Set`의 경우 순서가 보장되지 않는다.

```dart
Set<String> names = {'Alice', 'Bob', 'Charlie'};
for (var name in names) {
  print(name);
}
```

혹은 `forEach`를 사용하여 다음과 같이 처리할 수 있다:

```dart
names.forEach((name) {
  print(name);
});
```

#### Set의 변환 및 정렬

`Set`은 순서가 없는 컬렉션이므로, 정렬된 순서로 데이터를 처리하고자 할 때는 리스트로 변환한 후에 정렬할 수 있다. Dart에서는 `toList` 메서드를 사용하여 `Set`을 리스트로 변환할 수 있다:

```dart
Set<int> numbers = {5, 3, 1, 4, 2};
List<int> sortedList = numbers.toList()..sort();
print(sortedList); // [1, 2, 3, 4, 5]
```

#### Set과 중복 처리

`Set`은 중복된 원소를 허용하지 않는다. Dart에서 중복된 원소를 추가하려고 하면 자동으로 무시된다. 예를 들어, 동일한 값을 여러 번 추가해도 결과는 동일하다:

```dart
Set<int> numbers = {1, 2, 3};
numbers.add(3);
print(numbers); // {1, 2, 3}
```

이 특성 덕분에 `Set`은 중복을 제거해야 하는 상황에서 유용하다. 예를 들어, 리스트에 중복된 값이 있을 경우 이를 `Set`으로 변환하면 중복된 값이 자동으로 제거된다:

```dart
List<int> numberList = [1, 2, 2, 3, 4, 4, 5];
Set<int> uniqueNumbers = numberList.toSet();
print(uniqueNumbers); // {1, 2, 3, 4, 5}
```

#### Set의 크기와 비어 있는지 확인

Dart의 `Set`에서는 집합의 크기를 확인하기 위해 `length` 속성을 사용하고, 비어 있는지 확인하려면 `isEmpty`와 `isNotEmpty` 메서드를 사용할 수 있다:

```dart
Set<String> names = {'Alice', 'Bob', 'Charlie'};
print(names.length); // 3
print(names.isEmpty); // false
print(names.isNotEmpty); // true
```

이처럼 `Set`은 크기와 상태를 쉽게 파악할 수 있는 메서드를 제공하여 데이터를 효율적으로 관리할 수 있다.

#### Set의 추가 및 제거

`Set`은 리스트처럼 데이터를 동적으로 추가하거나 제거할 수 있다. Dart에서는 `add`와 `remove` 메서드를 사용하여 값을 추가하거나 삭제할 수 있다. 여러 개의 값을 한 번에 추가하거나 삭제하려면 `addAll`과 `removeAll`을 사용할 수 있다.

```dart
Set<int> numbers = {1, 2, 3};
numbers.add(4); // 4 추가
numbers.remove(2); // 2 제거
print(numbers); // {1, 3, 4}
```

여러 값을 추가하고 제거할 때는 다음과 같이 한다:

```dart
Set<int> numbers = {1, 2, 3};
numbers.addAll([4, 5, 6]); // 여러 값을 추가
numbers.removeAll([1, 2]); // 여러 값을 제거
print(numbers); // {3, 4, 5, 6}
```

#### Set의 복사

`Set`을 복사하려면 `Set.from` 생성자를 사용하거나, `toSet` 메서드를 통해 새로운 `Set`을 생성할 수 있다. 이는 원본 `Set`과 동일한 값을 가지는 새로운 `Set`을 만들지만, 서로 다른 객체로 관리된다.

```dart
Set<int> numbers = {1, 2, 3};
Set<int> copiedSet = Set.from(numbers);
print(copiedSet); // {1, 2, 3}
```

이처럼 Dart에서의 `Set`은 다양한 연산과 메서드를 통해 쉽게 데이터를 관리하고 조작할 수 있는 유용한 도구이다.
