Spliterator

김신영·2022년 9월 23일
0

stream

목록 보기
1/3
post-thumbnail

Spliterator

  • 분할할 수 있는 반복자
  • splitable iterator
public interface Spliterator<T> {
	boolean tryAdvance(Consumer<? super T> action);
	Spliterator<T> trySplit();
	long estimateSize();
	int characteristics();
}

tryAdvance

  • 현재 요소에 Consumer를 수행 후, 다음 요소가 있는지 리턴

trySplit

  • 분할이 불가능한 경우, null 리턴
  • 분할 가능한 경우, 분할시킨 새로 생성한 Spliterator 리턴
  • 분할하고 나머지 부분 계속해서 trySplit 시도

estimateSize

  • 탐색해야할 요소의 개수를 리턴

characteristic

CharacteristicDescription
ORDERED리스트처럼 요소에 정해진 순서가 있으므로 Spliterator는 요소를 탐색하고 분할할 때 이 순서에 유의해야한다.
DISTINCTx, y 두요소를 방문했을 때 x.equals(y)는 항상 false를 반환한다.
SORTED탐색된 요소는 미리 정의된 정렬 순서를 따른다.
SIZED크기가 알려진 소스로 Spliterator를 생성했으므로 estimatedSize()는 정확한 값을 반환한다.
NON-NULL탐색하는 모든 요소는 null이 아니다
IMMUTABLE모든 요소는 불변이다. 즉 요소를 탐색하는 동안 요소를 추가하거나, 삭제하거나, 수정할 수 없다.
CONCURRENT동기화 없이 Spliterator의 소스를 여러 쓰레드에서 동시에 고칠 수 있따.
SUBSIZEDSpliterator 자신 그리고 분할되는 모든 Spliterator는 SIZED 특성을 가진다.

Example

  • 문장에서 단어 갯수 구하기 (스페이스 문자 기준)
  • 입력값
    	> "Nel   mezzo del cammin  di nostra  vita mi  ritrovai in una  selva oscura che la  dritta via era   smarrita "
  • 정답은 19

1. Procedural Programming

public static int countWordsIteratively(String s) {  
  int counter = 0;  
  boolean lastSpace = true;  
  for (char c : s.toCharArray()) {  
    if (Character.isWhitespace(c)) {  
      lastSpace = true;  
    }  
    else {  
      if (lastSpace) {  
        counter++;  
      }  
      lastSpace = Character.isWhitespace(c);  
    }  
  }  
  return counter;  
}

Input

SENTENCE = "Nel   mezzo del cammin  di nostra  vita mi  ritrovai in una  selva oscura che la  dritta via era   smarrita "

Execution

public static main(String[] args) {
	System.out.println("Found " + countWordsIteratively(SENTENCE) + " words");
}

Execution Result

Found 19 words  // OK

2. Custom Reducing with Stream

class WordCounter {  
  private final int counter;  
  private final boolean lastSpace;  
  
  public WordCounter() {  
    this(0, true);  
  }  
  
  public WordCounter(int counter, boolean lastSpace) {  
    this.counter = counter;  
    this.lastSpace = lastSpace;  
  }  
  
  public WordCounter accumulate(Character c) {  
    if (Character.isWhitespace(c)) {  
      return lastSpace ? this : new WordCounter(counter, true);  
    }  
    else {  
      return lastSpace ? new WordCounter(counter + 1, false) : this;  
    }  
  }  
  
  public WordCounter combine(WordCounter wordCounter) {  
    return new WordCounter(counter + wordCounter.counter, wordCounter.lastSpace);  
  }  
  
  public int getCounter() {  
    return counter;  
  }  
}

Execution

public static int countWordsWithCustomReducing(String s, boolean isParallel) {  
  Stream<Character> stream = IntStream.range(0, s.length()).mapToObj(s::charAt);  
  
  if (isParallel) {  
    stream = stream.parallel();  
  }  
  
  return countWordsWithCustomReducing(stream);  
}

public static main(String[] args) {
	System.out.println("Found " + countWordsWithCustomReducing(SENTENCE, false) + " words");
}

Execution Result

Found 19 words  // OK

Execution (With Parallel Stream)

public static main(String[] args) {
	System.out.println("Found " + countWordsWithCustomReducing(SENTENCE, false) + " words");
}

Execution Result (With Parallel Stream)

  • Wrong Answer
Found 43 words  // Wrong

3. Custom Spliterator

private static class WordCounterSpliterator implements Spliterator<Character> {  
  private static final int SPLIT_BUCKET_SIZE = 10;  
  private final String string;  
  private int currentIndex = 0;  
  
  private WordCounterSpliterator(String string) {  
    this.string = string;  
  }  
  
  @Override  
  public boolean tryAdvance(Consumer<? super Character> action) {  
    action.accept(string.charAt(currentIndex++));  
    return currentIndex < string.length();  
  }  
  
  @Override  
  public Spliterator<Character> trySplit() {  
    int currentSize = string.length() - currentIndex;  
    if (currentSize < SPLIT_BUCKET_SIZE) {  
      return null;  
    }  
    for (int splitIndex = currentSize / 2 + currentIndex; splitIndex < string.length(); splitIndex++) {  
      if (Character.isWhitespace(string.charAt(splitIndex))) {  
        Spliterator<Character> spliterator =  
            new WordCounterSpliterator(string.substring(currentIndex, splitIndex));  
        currentIndex = splitIndex;  
        return spliterator;  
      }  
    }  
    return null;  
  }  
  
  @Override  
  public long estimateSize() {  
    return string.length() - currentIndex;  
  }  
  
  @Override  
  public int characteristics() {  
    return ORDERED + SIZED + SUBSIZED + NONNULL + IMMUTABLE;  
  }  
}

Execution

public static int countWordsWithSpliterator(String s) {  
  Spliterator<Character> spliterator = new WordCounterSpliterator(s);  
  Stream<Character> stream = StreamSupport.stream(spliterator, true);  
  return countWordsWithCustomReducing(stream);  
}

public static main(String[] args) {
	System.out.println("Found " + countWordsWithSpliterator(SENTENCE) + " words");
}

Execution Result

Found 19 words  // OK

Example (GroupSpliterator)

public static <T> Spliterator<Stream<T>> groupSpliterator(Spliterator<T> spliterator, int grouping) {  
    List<T> originalList = StreamSupport.stream(spliterator, true).collect(Collectors.toList());  
    return IntStream.rangeClosed(0, (originalList.size() - 1) / 3)  
        .parallel()  
        .mapToObj(g ->  
            IntStream.range(grouping * g, grouping * (g + 1))  
                .takeWhile(i -> i < originalList.size())  
                .mapToObj(originalList::get)  
        ).spliterator();  
}

public static void main(String... args) {  
    final List<String> data = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14");  
  
    Spliterator<Stream<String>> groupSpliterator = groupSpliterator(data.spliterator(), 3);  
  
    List<List<String>> groupResult = StreamSupport.stream(groupSpliterator, true)  
        .parallel()  
        .collect(Collectors.toList())  
        .stream()  
        .map(subStream -> subStream.collect(Collectors.toList()))  
        .collect(Collectors.toList());  
  
    // print result  
    groupResult.forEach(list -> {  
        final String joined = list.stream().collect(Collectors.joining(" "));  
        System.out.println("[" + joined + "]");  
    });  
}
profile
Hello velog!

0개의 댓글