1. 개요

이전에 Javaslang으로 알려진 Vavr 라이브러리는 Java용 기능 ​​라이브러리입니다. 이 기사에서는 강력한 컬렉션 API를 살펴봅니다.

이 라이브러리에 대한 자세한 내용은 이 문서 를 참조하십시오 .

2. 영구 수집

수정 시 영구 컬렉션은 현재 버전을 유지하면서 컬렉션의 새 버전을 생성합니다.

동일한 컬렉션의 여러 버전을 유지 관리하면 비효율적인 CPU 및 메모리 사용이 발생할 수 있습니다. 그러나 Vavr 컬렉션 라이브러리는 여러 버전의 컬렉션 간에 데이터 구조를 공유하여 이를 극복합니다.

이는 단순히 기본 컬렉션 주위에 래퍼를 제공하는 Collections 유틸리티 클래스 의 Java unmodifiableCollection()과 근본적 으로 다릅니다 .

이러한 컬렉션을 수정하려고 하면 새 버전이 생성되는 대신 UnsupportedOperationException 이 발생합니다. 또한 기본 컬렉션은 직접 참조를 통해 여전히 변경 가능합니다.

3. 횡단 가능

Traversable 은 모든 Vavr 컬렉션의 기본 유형입니다. 이 인터페이스는 모든 데이터 구조 간에 공유되는 메서드를 정의합니다.

size() , get() , filter() , isEmpty() 및 하위 인터페이스에서 상속되는 기타 유용한 기본 메서드를 제공합니다 .

컬렉션 라이브러리를 더 자세히 살펴보겠습니다.

4. 시퀀스

시퀀스부터 시작하겠습니다.

Seq 인터페이스는 순차 데이터 구조를 나타냅니다 . List , Stream , Queue , Array , VectorCharSeq 의 상위 인터페이스입니다 . 이러한 모든 데이터 구조에는 아래에서 살펴볼 고유한 속성이 있습니다.

4.1. List

List 은 LinearSeq 인터페이스 를 확장하는 요소 열심히 평가된 시퀀스입니다 .

영구 List 은 머리와 꼬리에서 재귀적으로 형성됩니다.

  • 머리 – 첫 번째 요소
  • Tail – 나머지 요소를 포함하는 List(해당 List도 머리와 꼬리로 구성됨)

List API에는 List 를 생성하는 데 사용할 수 있는 정적 팩터리 메서드가 있습니다 . 정적 of() 메서드를 사용하여 하나 이상의 개체에서 List 인스턴스를 만들 수 있습니다.

정적 empty() 를 사용하여 빈 List 을 만들고 ofAll() 을 사용 하여 Iterable 유형 에서 List 을 만들 수도 있습니다.

List<String> list = List.of(
  "Java", "PHP", "Jquery", "JavaScript", "JShell", "JAVA");

List을 조작하는 방법에 대한 몇 가지 예를 살펴보겠습니다.

drop() 과 그 변형을 사용하여 첫 번째 N 개의 요소를 제거할 수 있습니다.

List list1 = list.drop(2);                                      
assertFalse(list1.contains("Java") && list1.contains("PHP"));   
                                                                
List list2 = list.dropRight(2);                                 
assertFalse(list2.contains("JAVA") && list2.contains("JShell"));
                                                                
List list3 = list.dropUntil(s -> s.contains("Shell"));          
assertEquals(list3.size(), 2);                                  
                                                                
List list4 = list.dropWhile(s -> s.length() > 0);               
assertTrue(list4.isEmpty());

drop(int n) 은 첫 번째 요소부터 시작하여 List에서 n개의 요소를 제거 하는 반면 dropRight() 는 List의 마지막 요소부터 동일한 작업을 수행합니다.

dropUntil() 은 조건자가 true로 평가될 때까지 List에서 요소를 계속 제거하는 반면 dropWhile() 은 조건자가 true인 동안 요소를 계속 삭제합니다.

오른쪽에서 요소를 제거하기 시작하는 dropRightWhile ()dropRightUntil() 도 있습니다.

다음으로 take(int n) 은 List에서 요소를 가져오는 데 사용됩니다. List 에서 n개의 요소를 가져온 다음 중지합니다. List의 끝에서 요소를 가져오기 시작 하는 takeRight(int n) 도 있습니다.

List list5 = list.take(1);                       
assertEquals(list5.single(), "Java");            
                                                 
List list6 = list.takeRight(1);                  
assertEquals(list6.single(), "JAVA");            
                                                 
List list7 = list.takeUntil(s -> s.length() > 6);
assertEquals(list7.size(), 3);

마지막으로 takeUntil() 은 조건자가 참일 때까지 List에서 요소를 계속 가져옵니다. 술어 인수도 취하는 takeWhile() 변형이 있습니다 .

또한 API에는 다른 유용한 메서드가 있습니다. 예를 들어 중복되지 않는 요소의 List을 반환하는 distinct ()와 동등성을 결정하기 위해 Comparator 를 허용하는 distinctBy() 가 있습니다.

매우 흥미롭게도 List의 모든 요소 사이에 요소를 삽입하는 intersperse() 도 있습니다. 문자열 작업 에 매우 유용할 수 있습니다 .

List list8 = list
  .distinctBy((s1, s2) -> s1.startsWith(s2.charAt(0) + "") ? 0 : 1);
assertEquals(list8.size(), 2);

String words = List.of("Boys", "Girls")
  .intersperse("and")
  .reduce((s1, s2) -> s1.concat( " " + s2 ))
  .trim();  
assertEquals(words, "Boys and Girls");

List을 범주로 나누고 싶습니까? 이를 위한 API도 있습니다.

Iterator<List<String>> iterator = list.grouped(2);
assertEquals(iterator.head().size(), 2);

Map<Boolean, List<String>> map = list.groupBy(e -> e.startsWith("J"));
assertEquals(map.size(), 2);
assertEquals(map.get(false).get().size(), 1);
assertEquals(map.get(true).get().size(), 5);

group(int n)List 를 각각 n개의 요소 그룹으로 나눕니다 . groupdBy() 는 List을 나누는 논리를 포함 하는 함수 를 수락하고 두 항목( truefalse ) 이 포함된 Map 을 반환합니다 .

true함수 에 지정된 조건을 만족하는 요소 List 에 매핑됩니다. 거짓 키 는 그렇지 않은 요소 List 에 매핑됩니다 .

예상대로 List 를 변경할 때 원래 List 는 실제로 수정되지 않습니다. 대신 항상 새로운 버전의 List 가 반환됩니다.

또한 스택 의미론(LIFO(last-in-first-out) 요소 검색)을 사용 하여 List 와 상호 작용할 수 있습니다 . 이 정도까지는 peek() , pop()push( ) 와 같은 스택 조작을 위한 API 메서드가 있습니다 .

List<Integer> intList = List.empty();

List<Integer> intList1 = intList.pushAll(List.rangeClosed(5,10));

assertEquals(intList1.peek(), Integer.valueOf(10));

List intList2 = intList1.pop();
assertEquals(intList2.size(), (intList1.size() - 1) );

pushAll () 함수는 정수 범위를 스택에 삽입하는 데 사용되는 반면 peek() 는 스택의 헤드를 가져오는 데 사용됩니다. Option 개체 에 결과를 래핑할 수 있는 peekOption() 도 있습니다.

Java 문서 에 깔끔하게 문서화되어 있는 다른 흥미롭고 유용한 메소드가 List 인터페이스에 있습니다 .

4.2. 대기줄

불변 는 선입선출(FIFO) 검색을 허용하는 요소를 저장합니다.

Queue 는 내부적으로 앞 List 와 뒤 List 두 개의 연결된 List으로 구성됩니다 . 앞쪽 List 에는 Queue에서 빼낸 요소가 포함되고 뒤쪽 List 에는 Queue 에 포함된 요소가 포함됩니다.

이를 통해 Queue 에 넣기 및 Queue에서 빼기 작업을 O(1)에서 수행할 수 있습니다. 전면 List 에 요소가 부족하면 전면 및 후면 List 가 바뀌고 후면 List 가 반전됩니다.

큐를 만들어 봅시다:

Queue<Integer> queue = Queue.of(1, 2);
Queue<Integer> secondQueue = queue.enqueueAll(List.of(4,5));

assertEquals(3, queue.size());
assertEquals(5, secondQueue.size());

Tuple2<Integer, Queue<Integer>> result = secondQueue.dequeue();
assertEquals(Integer.valueOf(1), result._1);

Queue<Integer> tailQueue = result._2;
assertFalse(tailQueue.contains(secondQueue.get(0)));

dequeue 함수 는 Queue 에서 헤드 요소를 제거하고 Tuple2<T, Q> 를 반환합니다 . 튜플에는 제거된 헤드 요소가 첫 번째 항목으로 포함되고 Queue 의 나머지 요소가 두 번째 항목으로 포함됩니다.

조합(n) 을 사용 하여 Queue 에서 가능한 N 개의 요소 조합을 모두 얻을 수 있습니다 .

Queue<Queue<Integer>> queue1 = queue.combinations(2);
assertEquals(queue1.get(2).toCharSeq(), CharSeq.of("23"));

다시 말하지만 요소를 큐에 넣거나 빼는 동안 원래 Queue 가 수정되지 않는 것을 볼 수 있습니다 .

4.3. 개울

Stream 은 지연 연결 List 의 구현이며 java.util.stream 과는 상당히 다릅니다 . java.util.stream 과 달리 Vavr Stream 은 데이터를 저장하고 다음 요소를 느리게 평가합니다.

정수 스트림 이 있다고 가정해 보겠습니다 .

Stream<Integer> s = Stream.of(2, 1, 3, 4);

s.toString() 의 결과를 콘솔에 인쇄하면 Stream(2, ?) 만 표시됩니다 . 이것은 꼬리가 평가되지 않은 동안 평가 된 스트림 의 헤드만 평가됨을 의미합니다.

s.get(3) 을 호출 하고 이어서 s.tail() 의 결과를 표시하면 Stream(1, 3, 4, ?) 가 반환 됩니다. 반대로 s.get(3) 을 먼저 호출하지 않으면 Stream마지막 요소를 평가하도록 합니다 – s.tail() 의 결과는 Stream(1, ?) 뿐입니다 . 이는 꼬리의 첫 번째 요소만 평가되었음을 의미합니다.

이 동작은 성능을 향상시킬 수 있으며 Stream 을 사용 하여 (이론적으로) 무한히 긴 시퀀스를 나타낼 수 있습니다.

Vavr Stream 은 변경할 수 없으며 Empty 또는 Cons 일 수 있습니다 . Cons헤드 요소와 느리게 계산된 테일 Stream 으로 구성 됩니다. List달리 Stream 의 경우 헤드 요소만 메모리에 보관됩니다. 꼬리 요소는 요청 시 계산됩니다.

10개의 양의 정수로 구성된 스트림 을 만들고 짝수의 합을 계산해 보겠습니다 .

Stream<Integer> intStream = Stream.iterate(0, i -> i + 1)
  .take(10);

assertEquals(10, intStream.size());

long evenSum = intStream.filter(i -> i % 2 == 0)
  .sum()
  .longValue();

assertEquals(20, evenSum);

Java 8 Stream API와 달리 Vavr의 Stream 은 일련의 요소를 저장하기 위한 데이터 구조입니다.

따라서 get() , append() , insert() 및 요소를 조작하기 위한 기타 메서드가 있습니다. drop() , distinct () 및 이전에 고려한 다른 메서드도 사용할 수 있습니다.

마지막으로 Stream 에서 tabulate() 를 빠르게 보여드리겠습니다 . 이 메서드는 함수를 적용한 결과인 요소를 포함하는 길이 n 의 Stream 을 반환합니다.

Stream<Integer> s1 = Stream.tabulate(5, (i)-> i + 1);
assertEquals(s1.get(2).intValue(), 3);

또한 zip() 을 사용하여 Tuple2<Integer, Integer> 스트림생성 할 수 있습니다. 여기에는 두 개의 스트림 을 결합하여 구성되는 요소가 포함됩니다 .

Stream<Integer> s = Stream.of(2,1,3,4);

Stream<Tuple2<Integer, Integer>> s2 = s.zip(List.of(7,8,9));
Tuple2<Integer, Integer> t1 = s2.get(0);
 
assertEquals(t1._1().intValue(), 2);
assertEquals(t1._2().intValue(), 7);

4.4. 정렬

배열 은 효율적인 임의 액세스를 허용하는 불변의 인덱스 시퀀스입니다 . 객체 의 Java 배열 에 의해 지원됩니다 . 기본적으로 T 유형의 객체 배열에 대한 Traversable 래퍼입니다 .

정적 메서드 of() 를 사용하여 배열 을 인스턴스화할 수 있습니다 . 정적 range()rangeBy() 메서드 를 사용하여 범위 요소를 생성할 수도 있습니다 . rangeBy () 에는 단계를 정의할 수 있는 세 번째 매개변수가 있습니다.

range()rangeBy () 메서드는 시작 값에서 끝 값에서 1을 뺀 값까지만 요소를 생성합니다. 끝 값을 포함해야 하는 경우 rangeClosed() 또는 rangeClosedBy() 를 사용할 수 있습니다 .

Array<Integer> rArray = Array.range(1, 5);
assertFalse(rArray.contains(5));

Array<Integer> rArray2 = Array.rangeClosed(1, 5);
assertTrue(rArray2.contains(5));

Array<Integer> rArray3 = Array.rangeClosedBy(1,6,2);
assertEquals(rArray3.size(), 3);

인덱스로 요소를 조작해 보겠습니다.

Array<Integer> intArray = Array.of(1, 2, 3);
Array<Integer> newArray = intArray.removeAt(1);

assertEquals(3, intArray.size());
assertEquals(2, newArray.size());
assertEquals(3, newArray.get(1).intValue());

Array<Integer> array2 = intArray.replace(1, 5);
assertEquals(array2.get(0).intValue(), 5);

4.5. 벡터

벡터 는 배열List 사이에 있는 일종의 인덱싱된 요소 시퀀스를 제공하여 일정한 시간에 임의 액세스와 수정을 모두 허용합니다.

Vector<Integer> intVector = Vector.range(1, 5);
Vector<Integer> newVector = intVector.replace(2, 6);

assertEquals(4, intVector.size());
assertEquals(4, newVector.size());

assertEquals(2, intVector.get(1).intValue());
assertEquals(6, newVector.get(1).intValue());

4.6. CharSeq

CharSeq 는 기본 문자 시퀀스를 표현하는 컬렉션 개체입니다. 기본적으로 수집 작업이 추가 된 문자열 래퍼입니다.

CharSeq 를 생성하려면 :

CharSeq chars = CharSeq.of("vavr");
CharSeq newChars = chars.replace('v', 'V');

assertEquals(4, chars.size());
assertEquals(4, newChars.size());

assertEquals('v', chars.charAt(0));
assertEquals('V', newChars.charAt(0));
assertEquals("Vavr", newChars.mkString());

5. 설정

이 섹션에서는 컬렉션 라이브러리의 다양한 Set 구현에 대해 자세히 설명합니다. Set 데이터 구조 의 고유한 기능은 중복 값을 허용하지 않는다는 것입니다.

그러나 Set 의 다른 구현이 있습니다. HashSet 이 기본입니다. TreeSet중복 요소를 허용하지 않으며 정렬할 수 있습니다. LinkedHashSet해당 요소의 삽입 순서를 유지합니다.

이러한 구현을 하나씩 자세히 살펴보겠습니다.

5.1. 해시셋

HashSet 에는 새로운 인스턴스를 생성하기 위한 정적 팩토리 메서드가 있습니다. 이 중 일부는 of() , ofAll()range() 메서드의 변형과 같이 이 기사의 이전 부분에서 살펴보았습니다.

diff() 메서드 를 사용하여 두 세트 간의 차이를 얻을 수 있습니다 . 또한 union()intersect() 메서드는 두 집합의 합집합과 교집합을 반환합니다.

HashSet<Integer> set0 = HashSet.rangeClosed(1,5);
HashSet<Integer> set1 = HashSet.rangeClosed(3, 6);

assertEquals(set0.union(set1), HashSet.rangeClosed(1,6));
assertEquals(set0.diff(set1), HashSet.rangeClosed(1,2));
assertEquals(set0.intersect(set1), HashSet.rangeClosed(3,5));

요소 추가 및 제거와 같은 기본 작업도 수행할 수 있습니다.

HashSet<String> set = HashSet.of("Red", "Green", "Blue");
HashSet<String> newSet = set.add("Yellow");

assertEquals(3, set.size());
assertEquals(4, newSet.size());
assertTrue(newSet.contains("Yellow"));

HashSet 구현은 HAMT (Hash array mapped trie) 에 의해 지원되며 일반 HashTable 과 비교할 때 우수한 성능을 자랑 하며 그 구조는 영구 컬렉션을 지원하는 데 적합합니다.

5.2. 트리셋

변경할 수 없는 TreeSetSortedSet 인터페이스의 구현입니다. 정렬된 요소 집합 을 저장하고 이진 검색 트리를 사용하여 구현됩니다. 모든 작업은 O(log n) 시간에 실행됩니다.

기본적으로 TreeSet 의 요소는 자연 순서로 정렬됩니다.

자연스러운 정렬 순서를 사용 하여 SortedSet 을 만들어 보겠습니다 .

SortedSet<String> set = TreeSet.of("Red", "Green", "Blue");
assertEquals("Blue", set.head());

SortedSet<Integer> intSet = TreeSet.of(1,2,3);
assertEquals(2, intSet.average().get().intValue());

사용자 지정 방식으로 요소를 정렬하려면 TreeSet 을 만드는 동안 Comparator 인스턴스를 전달합니다 . 집합 요소에서 문자열을 생성할 수도 있습니다.

SortedSet<String> reversedSet
  = TreeSet.of(Comparator.reverseOrder(), "Green", "Red", "Blue");
assertEquals("Red", reversedSet.head());

String str = reversedSet.mkString(" and ");
assertEquals("Red and Green and Blue", str);

5.3. 비트셋

Vavr 컬렉션에는 변경할 수 없는 BitSet 구현도 포함되어 있습니다. BitSet 인터페이스는 SortedSet 인터페이스 를 확장합니다 . BitSet 은 BitSet.Builder 의 정적 메서드를 사용하여 인스턴스화할 수 있습니다 .

Set 데이터 구조 의 다른 구현과 마찬가지로 BitSet 은 중복 항목을 집합에 추가하는 것을 허용하지 않습니다.

Traversable 인터페이스 에서 조작을 위한 메서드를 상속합니다 . 표준 Java 라이브러리 의 java.util.BitSet 과 다릅니다 . BitSet 데이터는 문자열 값을 포함할 수 없습니다.

팩토리 메서드 of() 를 사용하여 BitSet 인스턴스 를 생성하는 방법을 살펴보겠습니다 .

BitSet<Integer> bitSet = BitSet.of(1,2,3,4,5,6,7,8);
BitSet<Integer> bitSet1 = bitSet.takeUntil(i -> i > 4);
assertEquals(bitSet1.size(), 4);

takeUntil() 을 사용하여 BitSet 의 처음 4개 요소를 선택합니다 . 작업이 새 인스턴스를 반환했습니다. takeUntil () 은 BitSet 의 상위 인터페이스인 Traversable 인터페이스 에 정의되어 있습니다.

Traversable 인터페이스에 정의된 위에서 설명한 다른 메서드 및 작업 도 BitSet 에 적용할 수 있습니다.

6. Map

맵은 키-값 데이터 구조입니다. Vavr의 은 변경 불가능하며 HashMap , TreeMapLinkedHashMap 에 대한 구현이 있습니다.

일반적으로 맵 계약은 중복 키를 허용하지 않지만 다른 키에 매핑된 중복 값이 ​​있을 수 있습니다.

6.1. 해시맵

HashMap 은 변경할 수 없는 Map 인터페이스 의 구현입니다. 키의 해시 코드를 사용하여 키-값 쌍을 저장합니다.

Vavr의 Tuple2 를 사용하여 기존 항목 유형 대신 키-값 쌍을 나타냅니다 .

Map<Integer, List<Integer>> map = List.rangeClosed(0, 10)
  .groupBy(i -> i % 2);
        
assertEquals(2, map.size());
assertEquals(6, map.get(0).get().size());
assertEquals(5, map.get(1).get().size());

HashSet유사하게 HashMap 구현은 거의 모든 작업에 대해 일정한 시간을 제공하는 HAMT(해시 배열 매핑 트리)에 의해 지원됩니다.

filterKeys() 메서드를 사용하여 키로 또는 filterValues() 메서드 를 사용하여 값으로 맵 항목을 필터링할 수 있습니다 . 두 메서드 모두 조건자를 인수로 허용합니다.

Map<String, String> map1
  = HashMap.of("key1", "val1", "key2", "val2", "key3", "val3");
        
Map<String, String> fMap
  = map1.filterKeys(k -> k.contains("1") || k.contains("2"));
assertFalse(fMap.containsKey("key3"));
        
Map<String, String> fMap2
  = map1.filterValues(v -> v.contains("3"));
assertEquals(fMap2.size(), 1);
assertTrue(fMap2.containsValue("val3"));

map() 메서드 를 사용하여 맵 항목을 변환할 수도 있습니다 . 예를 들어 map1Map<String, Integer> 로 변환해 보겠습니다 .

Map<String, Integer> map2 = map1.map(
  (k, v) -> Tuple.of(k, Integer.valueOf(v.charAt(v.length() - 1) + "")));
assertEquals(map2.get("key1").get().intValue(), 1);

6.2. 트리맵

변경할 수 없는 TreeMapSortedMap 인터페이스의 구현입니다. TreeSet유사하게 Comparator 인스턴스는 TreeMap 의 요소를 사용자 지정 정렬하는 데 사용됩니다 .

SortedMap 생성을 시연해 보겠습니다 .

SortedMap<Integer, String> map
  = TreeMap.of(3, "Three", 2, "Two", 4, "Four", 1, "One");

assertEquals(1, map.keySet().toJavaArray()[0]);
assertEquals("Four", map.get(4).get());

기본적으로 TreeMap 의 항목 은 키의 자연 순서대로 정렬됩니다. 그러나 정렬에 사용할 Comparator 를 지정할 수 있습니다.

TreeMap<Integer, String> treeMap2 =
  TreeMap.of(Comparator.reverseOrder(), 3,"three", 6, "six", 1, "one");
assertEquals(treeMap2.keySet().mkString(), "631");

TreeSet마찬가지로 TreeMap 구현도 트리를 사용하여 모델링되므로 해당 작업은 O(log n) 시간입니다. map.get(key)맵의 지정된 키에서 값을 래핑 하는 Option 을 반환합니다 .

7. Java와의 상호 운용성

수집 API는 Java의 수집 프레임워크와 완벽하게 상호 운용됩니다. 이것이 실제로 어떻게 수행되는지 봅시다.

7.1. 자바에서 Vavr로 변환

Vavr의 각 컬렉션 구현에는 java.util.Iterable 을 사용하는 정적 팩토리 메서드 ofAll()있습니다. 이를 통해 Java 컬렉션에서 Vavr 컬렉션을 만들 수 있습니다. 마찬가지로 또 다른 팩토리 메소드 ofAll() 은 Java 스트림 을 직접 사용합니다.

Java List 을 불변 List 으로 변환하려면 다음을 수행 하십시오.

java.util.List<Integer> javaList = java.util.Arrays.asList(1, 2, 3, 4);
List<Integer> vavrList = List.ofAll(javaList);

java.util.stream.Stream<Integer> javaStream = javaList.stream();
Set<Integer> vavrSet = HashSet.ofAll(javaStream);

또 다른 유용한 함수는 Vavr 컬렉션을 얻기 위해 Stream.collect() 와 함께 사용할 수 있는 collector() 입니다.

List<Integer> vavrList = IntStream.range(1, 10)
  .boxed()
  .filter(i -> i % 2 == 0)
  .collect(List.collector());

assertEquals(4, vavrList.size());
assertEquals(2, vavrList.head().intValue());

7.2. Vavr에서 Java로 변환

인터페이스에는 Vavr 유형을 Java 유형으로 변환하는 많은 방법이 있습니다. 이러한 메서드는 toJavaXXX() 형식 입니다.

몇 가지 예를 살펴보겠습니다.

Integer[] array = List.of(1, 2, 3)
  .toJavaArray(Integer.class);
assertEquals(3, array.length);

java.util.Map<String, Integer> map = List.of("1", "2", "3")
  .toJavaMap(i -> Tuple.of(i, Integer.valueOf(i)));
assertEquals(2, map.get("2").intValue());

Java 8 수집기 를 사용하여 Vavr 컬렉션에서 요소를 수집할 수도 있습니다.

java.util.Set<Integer> javaSet = List.of(1, 2, 3)
  .collect(Collectors.toSet());
        
assertEquals(3, javaSet.size());
assertEquals(1, javaSet.toArray()[0]);

7.3. 자바 컬렉션 뷰

또는 라이브러리는 Java 컬렉션으로 변환할 때 더 잘 수행되는 소위 컬렉션 뷰를 제공합니다. 이전 섹션의 변환 방법은 모든 요소를 ​​반복하여 Java 컬렉션을 빌드합니다.

반면에 보기는 표준 Java 인터페이스를 구현하고 기본 Vavr 컬렉션에 메서드 호출을 Delegation합니다.

이 글을 쓰는 시점에서는 List 보기만 지원됩니다. 각 순차 컬렉션에는 두 가지 방법이 있습니다. 하나는 변경 불가능한 보기를 만드는 것이고 다른 하나는 변경 가능한 보기를 만드는 것입니다.

불변 뷰에서 뮤테이터 메서드를 호출하면 UnsupportedOperationException 이 발생 합니다.

예를 살펴보겠습니다.

@Test(expected = UnsupportedOperationException.class)
public void givenVavrList_whenViewConverted_thenException() {
    java.util.List<Integer> javaList = List.of(1, 2, 3)
      .asJava();
    
    assertEquals(3, javaList.get(2).intValue());
    javaList.add(4);
}

변경할 수 없는 보기를 만들려면 다음 안내를 따르세요.

java.util.List<Integer> javaList = List.of(1, 2, 3)
  .asJavaMutable();
javaList.add(4);

assertEquals(4, javaList.get(3).intValue());

8. 결론

이 예제에서는 Vavr의 Collection API에서 제공하는 다양한 기능적 데이터 구조에 대해 배웠습니다. Vavr의 컬렉션 JavaDoc사용자 사용방법(예제) 에서 찾을 수 있는 더 유용하고 생산적인 API 메서드가 있습니다 .

마지막으로 라이브러리는 Value 인터페이스 를 확장하고 결과적으로 Java의 Iterable 인터페이스를 구현하는 Try , Option , EachFuture 도 정의한다는 점에 유의하는 것이 중요합니다 . 이는 일부 상황에서 모음으로 작동할 수 있음을 의미합니다.

이 기사의 모든 예제에 대한 전체 소스 코드는 Github 에서 찾을 수 있습니다 .

Generic footer banner