import lombok.Data;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.test.StepVerifier;
import reactor.util.function.Tuple2;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Stream;
public class FluxTest {
/**
* just創建數據流
*/
@Test
public void createAFluxJust() {
// 創建數據流,Flux裏邊數據結構是數組final T[] array
Flux<String> fruitFlux = Flux.just("Apple", "Orange", "Grape", "Banana", "Strawberry");
fruitFlux.subscribe(
s -> System.out.println("Here is some fruit:" + s)
);
// StepVerifier作爲校驗數據流的方法
StepVerifier.create(fruitFlux)
.expectNext("Apple")
.expectNext("Orange")
.expectNext("Grape")
.expectNext("Banana")
.expectNext("Strawberry")
.verifyComplete();
}
/**
* Iterable創建數據流
*/
@Test
public void createAFluxFromIterable() {
Flux<String> fruitFlux = Flux.fromIterable(Arrays.asList("Apple", "Orange", "Grape", "Banana", "Strawberry"));
fruitFlux.subscribe(
s -> System.out.println("Iterable創建數據流:" + s)
);
// StepVerifier作爲校驗數據流的方法
StepVerifier.create(fruitFlux)
.expectNext("Apple")
.expectNext("Orange")
.expectNext("Grape")
.expectNext("Banana")
.expectNext("Strawberry")
.verifyComplete();
}
/**
* Stream創建數據流
*/
@Test
public void createAFluxFromStream() {
Flux<String> fruitFlux = Flux.fromStream(Stream.of("Apple", "Orange", "Grape", "Banana", "Strawberry"));
fruitFlux.subscribe(
s -> System.out.println("Stream創建數據流:" + s)
);
// StepVerifier作爲校驗數據流的方法
StepVerifier.create(fruitFlux)
.expectNext("Apple")
.expectNext("Orange")
.expectNext("Grape")
.expectNext("Banana")
.expectNext("Strawberry")
.verifyComplete();
}
/**
* range創建數據流
*/
@Test
public void createAFluxRange() {
Flux<Integer> fruitFlux = Flux.range(1, 10);
fruitFlux.subscribe(
s -> System.out.println("range創建數據流:" + s)
);
// StepVerifier作爲校驗數據流的方法
StepVerifier.create(fruitFlux)
.expectNext(1)
.expectNext(2)
.expectNext(3)
.expectNext(4)
.expectNext(5)
.verifyComplete();
}
/**
* interval創建數據流
*/
@Test
public void createAFluxInterval() {
Flux<Long> fruitFlux = Flux.interval(Duration.ofSeconds(1))
// 限制最大值
.take(5);
fruitFlux.subscribe(
s -> System.out.println("interval創建數據流:" + s)
);
// StepVerifier作爲校驗數據流的方法
StepVerifier.create(fruitFlux)
.expectNext(0L)
.expectNext(1L)
.expectNext(2L)
.expectNext(3L)
.expectNext(4L)
.verifyComplete();
}
/**
* 合併但不創建流,並不能保證順序
*/
@Test
public void mergeFluxes() {
// 構建角色流
Flux<String> characterFlux = Flux
.just("Garfield", "Kojak", "Barbossa")
// 減慢發佈速度
.delayElements(Duration.ofMillis(100));
// 構建食物流
Flux<String> foodFlux = Flux
.just("Lasagna", "Lollipops", "Apples", "Garbage")
// 延緩發佈
.delaySubscription(Duration.ofMillis(250))
// 減慢發佈速度
.delayElements(Duration.ofMillis(500));
// merge
Flux<String> mergeFlux = characterFlux.mergeWith(foodFlux);
mergeFlux.subscribe(
s -> System.out.println("mergeFluxes數據流:" + s)
);
// StepVerifier作爲校驗數據流的方法,由於merge不能保證順序,
// 會報錯,所以使用delaySubscription推遲發佈
StepVerifier.create(mergeFlux)
.expectNext("Garfield")
.expectNext("Kojak")
.expectNext("Barbossa")
.expectNext("Lasagna")
.expectNext("Lollipops")
.expectNext("Apples")
.expectNext("Garbage")
.verifyComplete();
}
/**
* Flux靜態方法,交叉合併創建新的數據流
* 不會輸出Garbage
*/
@Test
public void zipFluxes() {
// 構建角色流
Flux<String> characterFlux = Flux
.just("Garfield", "Kojak", "Barbossa");
// 構建食物流
Flux<String> foodFlux = Flux
.just("Lasagna", "Lollipops", "Apples", "Garbage");
// merge
Flux<Tuple2<String, String>> zipFlux = Flux.zip(characterFlux, foodFlux);
zipFlux.subscribe(
s -> System.out.println("zipFluxes交叉合併數據流:" + s.getT1() + "---" + s.getT2())
);
}
/**
* Flux靜態方法,交叉合併創建新的數據流對象,
* 不會輸出Garbage
*/
@Test
public void zipFluxesToObject() {
// 構建角色流
Flux<String> characterFlux = Flux
.just("Garfield", "Kojak", "Barbossa");
// 構建食物流
Flux<String> foodFlux = Flux
.just("Lasagna", "Lollipops", "Apples", "Garbage");
// merge
Flux<String> zipFlux = Flux.zip(characterFlux, foodFlux, new BiFunction<String, String, String>() {
@Override
public String apply(String c, String f) {
return c + " eats " + f;
}
});
zipFlux.subscribe(
s -> System.out.println("zipFluxes交叉合併數據流:" + s)
);
}
/**
* 選擇第一個反應式類型進行發佈
*/
@Test
public void firstFlux() {
// delay needed to "slow down" the slow Flux
Flux<String> slowFlux = Flux.just("tortoise", "snail", "sloth")
.delaySubscription(Duration.ofMillis(100));
Flux<String> fastFlux = Flux.just("hare", "cheetah", "squirrel");
Flux<String> firstFlux = Flux.first(slowFlux, fastFlux);
StepVerifier.create(firstFlux)
.expectNext("hare")
.expectNext("cheetah")
.expectNext("squirrel")
.verifyComplete();
}
/**
* 從反應式類型中過濾數據
*/
@Test
public void skipAFew() {
Flux<String> countFlux = Flux.just(
"one", "two", "skip a few", "ninety nine", "one hundred")
.skip(3);
StepVerifier.create(countFlux)
.expectNext("ninety nine", "one hundred")
.verifyComplete();
}
/**
* 創建了一個在發佈值之前會等待4秒的Flux
*/
@Test
public void skipAFewSeconds() {
Flux<String> countFlux = Flux.just(
"one", "two", "skip a few", "ninety nine", "one hundred")
.delayElements(Duration.ofSeconds(1))
.skip(Duration.ofSeconds(4));
StepVerifier.create(countFlux)
.expectNext("ninety nine", "one hundred")
.verifyComplete();
}
/**
* take操作只發布第一批指定數量的數據項
*/
@Test
public void take() {
Flux<String> nationalParkFlux = Flux.just(
"Yellowstone", "Yosemite", "Grand Canyon", "Zion", "Acadia")
.take(3);
StepVerifier.create(nationalParkFlux)
.expectNext("Yellowstone", "Yosemite", "Grand Canyon")
.verifyComplete();
}
/**
* 訂閱之後的前3.5秒發佈數據條目
*/
@Test
public void takeForAwhile() {
Flux<String> nationalParkFlux = Flux.just(
"Yellowstone", "Yosemite", "Grand Canyon", "Zion", "Grand Teton")
.delayElements(Duration.ofSeconds(1))
.take(Duration.ofMillis(3500));
StepVerifier.create(nationalParkFlux)
.expectNext("Yellowstone", "Yosemite", "Grand Canyon")
.verifyComplete();
}
@Test
public void filter() {
Flux<String> nationalParkFlux = Flux.just(
"Yellowstone", "Yosemite", "Grand Canyon", "Zion", "Grand Teton")
.filter(np -> !np.contains(" "));
StepVerifier.create(nationalParkFlux)
.expectNext("Yellowstone", "Yosemite", "Zion")
.verifyComplete();
}
@Test
public void distinct() {
Flux<String> animalFlux = Flux.just(
"dog", "cat", "bird", "dog", "bird", "anteater")
.distinct();
StepVerifier.create(animalFlux)
.expectNext("dog", "cat", "bird", "anteater")
.verifyComplete();
}
@Test
public void map() {
Flux<FluxTest.Player> playerFlux = Flux
.just("Michael Jordan", "Scottie Pippen", "Steve Kerr")
.map(n -> {
String[] split = n.split("\\s");
return new FluxTest.Player(split[0], split[1]);
});
StepVerifier.create(playerFlux)
.expectNext(new FluxTest.Player("Michael", "Jordan"))
.expectNext(new FluxTest.Player("Scottie", "Pippen"))
.expectNext(new FluxTest.Player("Steve", "Kerr"))
.verifyComplete();
}
/**
* flatMap並不像map操作那樣簡單地將一個對象轉換到另一個對象,
* 而是將對象轉換爲新的Mono或Flux。結果形成的Mono或Flux會扁平化爲新的Flux。
* 當與subscribeOn()方法結合使用時,flatMap操作可以釋放Reactor反應式的異步能力。
* <p>
* 使用flatMap()和subscribeOn()的好處是:我們可以在多個並行線程之間拆分工作,
* 從而增加流的吞吐量。因爲工作是並行完成的,無法保證哪項工作首先完成,
* 所以結果Flux中數據項的發佈順序是未知的。
* Schedulers.parallel()是使用了
* Executors.newSingleThreadScheduledExecutor()啓動線程,
* 線程數量Runtime.getRuntime().availableProcessors()
*/
@Test
public void flatMap() {
Flux<FluxTest.Player> playerFlux = Flux
.just("Michael Jordan", "Scottie Pippen", "Steve Kerr")
.flatMap(n -> Mono.just(n)
.map(p -> {
String[] split = p.split("\\s");
return new FluxTest.Player(split[0], split[1]);
})
// 它聲明每個訂閱都應該在並行線程中進行,
// 因此可以異步並行地執行多個String對象的轉換操作
.subscribeOn(Schedulers.parallel())
);
playerFlux.subscribe(
s -> System.out.println("mergeFluxes數據流:" + s)
);
List<Player> playerList = Arrays.asList(
new FluxTest.Player("Michael", "Jordan"),
new FluxTest.Player("Scottie", "Pippen"),
new FluxTest.Player("Steve", "Kerr"));
StepVerifier.create(playerFlux)
.expectNextMatches(p -> playerList.contains(p))
.expectNextMatches(p -> playerList.contains(p))
.expectNextMatches(p -> playerList.contains(p))
.verifyComplete();
}
@Data
private static class Player {
private final String firstName;
private final String lastName;
}
/**
* 在反應式流上緩存數據
*/
@Test
public void buffer() {
Flux<String> fruitFlux = Flux.just(
"apple", "orange", "banana", "kiwi", "strawberry");
Flux<List<String>> bufferedFlux = fruitFlux.buffer(3);
StepVerifier
.create(bufferedFlux)
.expectNext(Arrays.asList("apple", "orange", "banana"))
.expectNext(Arrays.asList("kiwi", "strawberry"))
.verifyComplete();
}
@Test
public void bufferAndFlatMap() {
Flux.just(
"apple", "orange", "banana", "kiwi", "strawberry")
.buffer(3)
.flatMap(x ->
Flux.fromIterable(x)
.map(String::toUpperCase)
.subscribeOn(Schedulers.parallel())
.log()
).subscribe();
}
@Test
public void collectList() {
Flux<String> fruitFlux = Flux.just(
"apple", "orange", "banana", "kiwi", "strawberry");
Mono<List<String>> fruitListMono = fruitFlux.collectList();
StepVerifier
.create(fruitListMono)
.expectNext(Arrays.asList(
"apple", "orange", "banana", "kiwi", "strawberry"))
.verifyComplete();
}
@Test
public void collectMap() {
Flux<String> animalFlux = Flux.just(
"aardvark", "elephant", "koala", "eagle", "kangaroo");
Mono<Map<Character, String>> animalMapMono =
animalFlux.collectMap(a -> a.charAt(0));
StepVerifier
.create(animalMapMono)
.expectNextMatches(map -> {
return
map.size() == 3 &&
map.get('a').equals("aardvark") &&
map.get('e').equals("eagle") &&
map.get('k').equals("kangaroo");
})
.verifyComplete();
}
@Test
public void all() {
Flux<String> animalFlux = Flux.just(
"aardvark", "elephant", "koala", "eagle", "kangaroo");
Mono<Boolean> hasAMono = animalFlux.all(a -> a.contains("a"));
StepVerifier.create(hasAMono)
.expectNext(true)
.verifyComplete();
Mono<Boolean> hasKMono = animalFlux.all(a -> a.contains("k"));
StepVerifier.create(hasKMono)
.expectNext(false)
.verifyComplete();
}
@Test
public void any() {
Flux<String> animalFlux = Flux.just(
"aardvark", "elephant", "koala", "eagle", "kangaroo");
Mono<Boolean> hasAMono = animalFlux.any(a -> a.contains("a"));
StepVerifier.create(hasAMono)
.expectNext(true)
.verifyComplete();
Mono<Boolean> hasZMono = animalFlux.any(a -> a.contains("z"));
StepVerifier.create(hasZMono)
.expectNext(false)
.verifyComplete();
}
}