jquery autocompleetablefuture 默认多少县城

3961人阅读
javaSE及jsp(67)
CompletableFuture是java8中添加的一个类了,这个类主要的作用就是提供了新的方式来完成异步处理,包括合成和组合事件的非阻塞方式。
简单示例:
对于Future可以这样提交任务:
使用CompletableFuture:
这里,CompletableFuture和Future的get()调用都会阻塞,但是可以CompletableFuture注册类似一个回调函数去处理结果:
运行结果:
可以看出来,在CompletableFuture注册的回调函数的执行与其提交的任务的执行是同一个线程完成的。如果不想同一个线程来完成回调函数,可以这样:
这里第二个参数executor也可以使用其它的线程池,当然如果不指定第二个参数,即
这时会使用monPool这个线程池。
可以对通过completeExceptionally 函数对CompletableFuture发出异常通知:
运行结果:
可以发现thenAccept注册的回调函数不再执行,但是任务还是会执行完。
可以显式声明来处理异常:
运行结果:
但是,如果exceptionally的声明提前了呢?
注意到,两次exceptionally的参数Function &T, R&的类型值是不一样的,运行结果:
可以看出,在thenAccept前声明的exceptionally会有一个返回值传递给thenAccept。
创造和获取CompletableFuture
转换和作用于CompletableFuture(thenApply)
CompletableFuture优于Future,因为CompletableFuture是一个原子也是一个因子。Scala和JavaScript都允许future完成时允许注册异步回调,直到它准备好才要等待和阻止它。可以简单地说:运行这个函数时就出现了结果。此外,可以叠加这些功能,把多个future通过thenApply()组合在一起:
运行结果:
注意,不是以Async结尾的方法将在future完成的相同线程中调用该方法中的参数,而以Async结尾的方法将在不同的线程池中异步地调用方法中的参数。
运行完成的代码(thenAccept/thenRun)
在future的管道里有两种典型的“最终”阶段方法,可以理解为回调函数。…Async变量也可用两种方法,隐式和显式执行器,thenAccept()/thenRun()方法并没有发生阻塞(即使没有明确的executor)。它们像一个事件侦听器/处理程序。
单个CompletableFuture的错误处理
exceptionally()接受一个函数时,将调用原始future来抛出一个异常。这里会有机会将此异常转换为和Future类型的兼容的一些值来进行恢复。safe进一步的转换将不再产生一个异常而是从提供功能的函数返回一个String值。一个更加灵活的方法是handle()接受一个函数,它接收正确的结果或异常:
handle()总是被调用,结果和异常都非空,这是个一站式全方位的策略。
结合(链接)两个futures(thenCompose())
有时想运行一些future的值(当它准备好了),但这个函数也返回了future。CompletableFuture足够灵活地明白函数结果现在应该作为顶级的future,对比
仔细观察thenApply()(map)和thenCompose()(flatMap)的类型和差异.thenCompose()是一个重要的方法允许构建健壮的和异步的管道,没有阻塞和等待的中间步骤。
两个futures的转换值(thenCombine())
当thenCompose()用于链接一个future时依赖另一个thenCombine,当他们都完成之后就结合两个独立的futures:
假设有两个CompletableFuture,一个加载Customer另一个加载最近的Shop。他们彼此完全独立,但是当他们完成时,想要使用它们的值来计算Route。这是一个可剥夺的例子:
注意,在Java 8中可以用(cust, shop) -& findRoute(cust, shop)简单地代替this::findRoute方法的引用:
有customerFuture 和 shopFuture。那么routeFuture包装它们然后“等待”它们完成。当他们准备好了,它会运行提供的函数来结合所有的结果 (findRoute())。当两个基本的futures完成并且 findRoute()也完成时,这样routeFuture将会完成。
等待所有的 CompletableFutures 完成
如果不是产生新的CompletableFuture连接这两个结果,我们只是希望当完成时得到通知,我们可以使用 thenAcceptBoth()/runAfterBoth()系列的方法。它们的工作方式与thenAccept() 和 thenRun()类似,但是是等待两个futures而不是一个:
运行结果:
好了,你当然可以这么做。但是最关键的一点是CompletableFuture是允许异步的,它是事件驱动的编程模型而不是阻塞并急切地等待着结果。所以在功能上,上面两部分代码是等价的,但后者没有必要占用一个线程来执行。
等待第一个 CompletableFuture 来完成任务
另一个有趣的事是CompletableFutureAPI可以等待第一个(与所有相反)完成的future。当有两个相同类型任务的结果时就显得非常方便,只要关心响应时间就行了,没有哪个任务是优先的。
作为一个例子,当有两个系统可以集成。一个具有较小的平均响应时间但是拥有高的标准差,另一个一般情况下较慢,但是更加容易预测。为了两全其美(性能和可预测性)可以在同一时间调用两个系统并等着谁先完成。通常这会是第一个系统,但是在进度变得缓慢时,第二个系统就可以在可接受的时间内完成:
完整地转换第一个系统
applyToEither()算是 acceptEither()的前辈了。当两个futures快要完成时,后者只是简单地调用一些代码片段,applyToEither()将会返回一个新的future。当这两个最初的futures完成时,新的future也会完成。
多种结合的CompletableFuture
知道如何等待两个future来完成(使用thenCombine())并第一个完成(applyToEither())。但它可以扩展到任意数量的futures吗?的确,使用static辅助方法:
allOf()当所有的潜在futures完成时,使用了一个futures数组并且返回一个future(等待所有的障碍)。另一方面 anyOf()将会等待最快的潜在futures。
可以对CompletableFuture指定处理完成的时间,如果按时完成则通知,否则抛出超时异常并处理。
http://blog.csdn.net/zero__007/article/details/
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:467643次
积分:4919
积分:4919
排名:第4989名
原创:54篇
转载:177篇
评论:37条
(6)(16)(3)(2)(5)(21)(6)(3)(6)(33)(1)(24)(24)(1)(1)(1)(1)(28)(2)(1)(6)(1)(1)(2)(16)(1)(1)(1)(1)(4)(5)(5)(1)(1)(1)(2)Java异步编程(5)
在研究vertx线程模型的时候我开始注意到在vertx内部提供给开发者的多数异步api中用到了CompletableFuture或者Promise或者Future。以前也用过CompletableFuture和Futrue等,但当时没有想过它执行时的线程情况,于是写了个测试类用于了解它:
public class VertxTest {
Logger logger = LoggerFactory.getLogger(VertxTest.class);
public void test() {
System.out.println(Thread.currentThread().getName() + & | start&);
testAsync(1000, result -& {
System.out.println(Thread.currentThread().getName() + & || & + result);
CompletableFuture future1 = testAsync(10000);
future1.thenAccept(result -& {
System.out.println(Thread.currentThread().getName() + &|||& + result);
System.out.println(Thread.currentThread().getName() + &|||| end...& );
while (true) {}
private void testAsync(int max, Handler&AsyncResult&Float&& handler) {
Thread thread = new Thread(() -& {
float result = 0;
for (int i = 0; i & i++) {
result += 0.5;
handler.handle(Future.succeededFuture(result));
thread.start();
private CompletableFuture testAsync(int max) {
CompletableFuture future = new CompletableFuture();
new Thread(() -& {
System.out.println(Thread.currentThread().getName() + & inner Thread&);
float result = 0;
for (int i = 0; i & i++) {
result += 0.5;
plete(result);
}).start();
在我电脑上的执行结果:
main | start
Thread-0 || 500.0
Thread-1 inner Thread
main|||5000.0
main|||| end...
Thread-0 || 500.0和
Thread-1 inner Thread很好理解,在我new的那个线程中执行。
main|||5000.0
会有个问题:当把参数从10000调到时,我电脑上的结果是:
main | start
Thread-0 || 500.0
Thread-1 inner Thread
main|||| end...
Thread-1|||
请注意,对于
future1.thenAccept(result -& {
System.out.println(Thread.currentThread().getName() + &|||& + result);
中的那句打印,也就是传进去的这个Handler实例的handle(String param)调用,是在Thread-1中。而上面参数是10000时这个调用在main线程里。
为什么会有这个不同呢,或者说什么机制在调用future1.thenAccept(Handler handler)时,决定handler.handler(...)的调用要在哪个线程?
查看thenAccept的源代码,跟踪进去看到下面的片段:
private CompletableFuture&Void& uniAcceptStage(Executor e,
Consumer&? super T& f) {
if (f == null) throw new NullPointerException();
CompletableFuture&Void& d = new CompletableFuture&Void&();
if (e != null || !d.uniAccept(this, f, null)) {
UniAccept&T& c = new UniAccept&T&(e, d, this, f);
c.tryFire(SYNC);
这里的逻辑就是,如果调用thenAccept方法时,结果已经得到了,则直接返回,由当前调用thenAccept的线程调用handler.handle(...),否则会将传进来的handler包装成一个UniAccept&T&对象,放入到一个stack里,等待我new的那个线程执行完之后回调handler.handle(...),也就不在main线程中了。
这就解释了为什么我测试例子中当参数调大之后不是在main中执行那个打印,而是在一个新的线程中的缘故。CompletableFuture这么做的好处在于,对时间短的处理,直接返回结果,而对于“询问”结果时还没有计算出来的,则可以非阻塞的继续往下执行等待结果执行完了回调。
对于多线程编程,清楚你写的代码块在哪个线程上下文环境中执行是很重要的,所以了解了CompletableFuture的thenAccept()方法的这个机制,将会大有好处。CompletableFuture还提供了很多有用的方法,其机制也可以和thenAccept机制比较思考。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:10498次
排名:千里之外
(1)(1)(3)(1)(3)JAVA(37)
Java 8来了,是时候学一下新的东西了。Java 7和Java 6只不过是稍作修改的版本,而Java 8将会发生重大的改进。或许是Java 8太大了吧?今天我会给你彻底地解释JDK 8中的新的抽象 – CompletableFuture。众所周知,Java 8不到一年就会发布,因此这篇文章是基于的。&extends&提供了方法,一元操作符和促进异步性以及事件驱动编程模型,它并不止步于旧版本的Java中。如果你打开你一定会感到震惊。大约有五十种方法(!),而且它们中的一些非常有意思而且不好理解,例如:
&U,V& CompletableFuture&V& thenCombineAsync(
&&&&CompletableFuture&?
&&&&BiFunction&?
U,? extends
&&&&Executor
不必担心,继续读下去。CompletableFuture收集了所有&和的特征。此外,内置的lambda表达式使它更接近于。这听起来好得令人难以置信,但是请继续读下去。CompletableFuture有两个主要的方面优于ol中的Future&– 异步回调/转换,这能使得从任何时刻的任何线程都可以设置CompletableFuture的值。
提取、修改包装的值
通常futures代表其它线程中运行的代码,但事实并非总是如此。有时你想要创造一个Future来表示你知道将会发生什么,例如。所以你有Future但是未来并没有潜在的异步工作。你只是想在未来JMS消息到达时简单地完成(解决),这是由一个事件驱动的。在这种情况下,你可以简单地创建CompletableFuture来返还给你的客户端,只要你认为你的结果是可用的,仅仅通过complete()就能解锁所有等待Future的客户端。
首先你可以简单地创建新的CompletableFuture并且给你的客户端:
CompletableFuture&String& ask() {
CompletableFuture&String& future = new
CompletableFuture&&();
&&&&return
注意这个future和Callable没有任何联系,没有线程池也不是异步工作。如果现在客户端代码调用ask().get()它将永远阻塞。如果寄存器完成回调,它们就永远不会生效了。所以关键是什么?现在你可以说:
plete(&42&)
…此时此刻所有客户端Future.get()将得到字符串的结果,同时完成回调以后将会立即生效。当你想代表Future的任务时是非常方便的,而且没有必要去计算一些执行线程的任务上。plete()只能调用一次,后续调用将被忽略。但也有一个后门叫做CompletableFuture.obtrudeValue(…)覆盖一个新Future之前的价值,请小心使用。
有时你想要看到信号发生故障的情况,如你所知Future对象可以处理它所包含的结果或异常。如果你想进一步传递一些异常,可以用pleteExceptionally(ex) (或者用obtrudeException(ex)这样更强大的方法覆盖前面的异常)。 completeExceptionally()也能解锁所有等待的客户端,但这一次从get()抛出异常。说到get(),也有CompletableFuture.join()方法在错误处理方面有着细微的变动。但总体上,它们都是一样的。最后也有CompletableFuture.getNow(valueIfAbsent)方法没有阻塞但是如果Future还没完成将返回默认值,这使得当构建那种我们不想等太久的健壮系统时非常有用。
最后static的方法是用completedFuture(value)来返回已经完成Future的对象,当测试或者写一些适配器层时可能非常有用。
创造和获取CompletableFuture
好了,那么手动地创建CompletableFuture是我们唯一的选择吗?不一定。就像一般的Futures,我们可以关联存在的任务,同时CompletableFuture使用工厂方法:
&U& CompletableFuture&U& supplyAsync(Supplier&U& supplier);
&U& CompletableFuture&U& supplyAsync(Supplier&U& supplier, Executor executor);
CompletableFuture&Void& runAsync(Runnable runnable);
CompletableFuture&Void& runAsync(Runnable runnable, Executor executor);
无参方法Executor是以…Async结尾同时将会使用monPool()(全局的,在JDK8中介绍的通用池),这适用于CompletableFuture类中的大多数的方法。runAsync()易于理解,注意它需要Runnable,因此它返回CompletableFuture&Void&作为Runnable不返回任何值。如果你需要处理异步操作并返回结果,使用Supplier&U&:
CompletableFuture&String& future = CompletableFuture.supplyAsync(new
Supplier&String&() {
&&&&@Override
&&&&public
String get() {
&&&&&&&&return
executor);
但是别忘了,Java 8里面还有lambdas表达式呢!
finalCompletableFuture&String&
future = CompletableFuture.supplyAsync(() -& {
&&&&return
executor);
CompletableFuture&String& future =
&&&&CompletableFuture.supplyAsync(()
-& longRunningTask(params), executor);
虽然这篇文章不是关于Lambda的,但是我会相当频繁地使用lambda表达式。
转换和作用于CompletableFuture(thenApply)
我说过CompletableFuture优于Future但是你还不知道为什么吗?简单说,因为CompletableFuture是一个原子也是一个因子。我说的这句话没什么帮助吗?Scala和JavaScript都允许future完成时允许注册异步回调,直到它准备好我们才要等待和阻止它。我们可以简单地说:运行这个函数时就出现了结果。此外,我们可以叠加这些功能,把多个future组合在一起等。例如如果我们从String转为Integer,我们可以转为在不关联的前提下从CompletableFuture到 CompletableFuture&Integer。这是通过thenApply()的方法:
CompletableFuture&U& thenApply(Function&? super
T,? extends
CompletableFuture&U& thenApplyAsync(Function&? super
T,? extends
CompletableFuture&U& thenApplyAsync(Function&? super
T,? extends
U& fn, Executor executor);&p&&/p&
&p&如前所述...Async版本提供对CompletableFuture的大多数操作,因此我将在后面的部分中跳过它们。记住,第一个方法将在future完成的相同线程中调用该方法,而剩下的两个将在不同的线程池中异步地调用它。
让我们来看看thenApply()的工作流程:&/p&
class=&brush:
gutter: first-line: 1; highlight: []; html-script: false&&
CompletableFuture&String&
CompletableFuture&Integer&
f2 = f1.thenApply(Integer::parseInt);
CompletableFuture&Double&
f3 = f2.thenApply(r -& r * r * Math.PI);
或在一个声明中:
CompletableFuture&Double&
&&&&f1.thenApply(Integer::parseInt).thenApply(r
-& r * r * Math.PI);
这里,你会看到一个序列的转换,从String到Integer再到Double。但最重要的是,这些转换既不立即执行也不停止。这些转换既不立即执行也不停止。他们只是记得,当原始f1完成他们所执行的程序。如果某些转换非常耗时,你可以提供你自己的Executor来异步地运行他们。注意,此操作相当于Scala中的一元map。
运行完成的代码(thenAccept/thenRun)
CompletableFuture&Void&
thenAccept(Consumer&? super
T& block);
CompletableFuture&Void&
thenRun(Runnable action);
在future的管道里有两种典型的“最终”阶段方法。他们在你使用future的值的时候做好准备,当 thenAccept()提供最终的值时,thenRun执行 Runnable,这甚至没有方法去计算值。例如:
future.thenAcceptAsync(dbl
-& log.debug(&Result:
dbl), executor);
log.debug(&Continuing&);
…Async变量也可用两种方法,隐式和显式执行器,我不会过多强调这个方法。
thenAccept()/thenRun()方法并没有发生阻塞(即使没有明确的executor)。它们像一个事件侦听器/处理程序,你连接到一个future时,这将执行一段时间。”Continuing”消息将立即出现,尽管future甚至没有完成。
单个CompletableFuture的错误处理
到目前为止,我们只讨论计算的结果。那么异常呢?我们可以异步地处理它们吗?当然!
CompletableFuture&String&
&&&&future.exceptionally(ex
have a problem: &
+ ex.getMessage());
exceptionally()接受一个函数时,将调用原始future来抛出一个异常。我们会有机会将此异常转换为和Future类型的兼容的一些值来进行恢复。safe进一步的转换将不再产生一个异常而是从提供功能的函数返回一个String值。
一个更加灵活的方法是handle()接受一个函数,它接收正确的结果或异常:
CompletableFuture&Integer&
safe = future.handle((ok, ex) -& {
(ok != null)
&&&&&&&&return
Integer.parseInt(ok);
&&&&&&&&log.warn(&Problem&,
&&&&&&&&return
handle()总是被调用,结果和异常都非空,这是个一站式全方位的策略。
一起结合两个CompletableFuture
异步处理过程之一的CompletableFuture非常不错但是当多个这样的futures以各种方式组合在一起时确实显示了它的强大。
结合(链接)这两个futures(thenCompose())
有时你想运行一些future的值(当它准备好了),但这个函数也返回了future。CompletableFuture足够灵活地明白我们的函数结果现在应该作为顶级的future,对比CompletableFuture&CompletableFuture&。方法 thenCompose()相当于Scala的flatMap:
CompletableFuture&U& thenCompose(Function&? super
T,CompletableFuture&U&& fn);
…Async变化也是可用的,在下面的事例中,仔细观察thenApply()(map)和thenCompose()(flatMap)的类型和差异,当应用calculateRelevance()方法返回CompletableFuture:
CompletableFuture&Document&
docFuture =
CompletableFuture&CompletableFuture&Double&&
&&&&docFuture.thenApply(this::calculateRelevance);
CompletableFuture&Double&
relevanceFuture =
&&&&docFuture.thenCompose(this::calculateRelevance);
CompletableFuture&Double& calculateRelevance(Document doc)&
thenCompose()是一个重要的方法允许构建健壮的和异步的管道,没有阻塞和等待的中间步骤。
两个futures的转换值(thenCombine())
当thenCompose()用于链接一个future时依赖另一个thenCombine,当他们都完成之后就结合两个独立的futures:
CompletableFuture&V& thenCombine(CompletableFuture&? extends
U& other, BiFunction&? super
U,? extends
…Async变量也是可用的,假设你有两个CompletableFuture,一个加载Customer另一个加载最近的Shop。他们彼此完全独立,但是当他们完成时,您想要使用它们的值来计算Route。这是一个可剥夺的例子:
CompletableFuture&Customer&
customerFuture = loadCustomerDetails(123);
CompletableFuture&Shop&
shopFuture = closestShop();
CompletableFuture&Route&
routeFuture =
&&&&customerFuture.thenCombine(shopFuture,
(cust, shop) -& findRoute(cust, shop));
Route findRoute(Customer customer, Shop shop)
请注意,在Java 8中可以用(cust, shop) -& findRoute(cust, shop)简单地代替this::findRoute方法的引用:
customerFuture.thenCombine(shopFuture,
this::findRoute);
你也知道,我们有customerFuture 和 shopFuture。那么routeFuture包装它们然后“等待”它们完成。当他们准备好了,它会运行我们提供的函数来结合所有的结果(findRoute())。当两个基本的futures完成并且 findRoute()也完成时,这样routeFuture将会完成。
等待所有的 CompletableFutures 完成
如果不是产生新的CompletableFuture连接这两个结果,我们只是希望当完成时得到通知,我们可以使用thenAcceptBoth()/runAfterBoth()系列的方法,(…Async 变量也是可用的)。它们的工作方式与thenAccept() 和 thenRun()类似,但是是等待两个futures而不是一个:
CompletableFuture&Void& thenAcceptBoth(CompletableFuture&? extends
U& other, BiConsumer&? super
CompletableFuture&Void&
runAfterBoth(CompletableFuture&?& other, Runnable action)
想象一下上面的例子,这不是产生新的 CompletableFuture,你只是想要立刻发送一些事件或刷新GUI。这可以很容易地实现:thenAcceptBoth():
customerFuture.thenAcceptBoth(shopFuture,
(cust, shop) -& {
Route route = findRoute(cust, shop);
我希望我是错的,但也许有些人会问自己一个问题:为什么我不能简单地阻塞这两个futures呢? 就像:
Future&Customer&
customerFuture = loadCustomerDetails(123);
Future&Shop&
shopFuture = closestShop();
findRoute(customerFuture.get(),
shopFuture.get());
好了,你当然可以这么做。但是最关键的一点是CompletableFuture是允许异步的,它是事件驱动的编程模型而不是阻塞并急切地等待着结果。所以在功能上,上面两部分代码是等价的,但后者没有必要占用一个线程来执行。
等待第一个 CompletableFuture 来完成任务
另一个有趣的事是CompletableFutureAPI可以等待第一个(与所有相反)完成的future。当你有两个相同类型任务的结果时就显得非常方便,你只要关心响应时间就行了,没有哪个任务是优先的。API方法(…Async变量也是可用的):
CompletableFuture&Void&
acceptEither(CompletableFuture&? extends
T& other, Consumer&? super
CompletableFuture&Void&
runAfterEither(CompletableFuture&?& other, Runnable action)
作为一个例子,你有两个系统可以集成。一个具有较小的平均响应时间但是拥有高的标准差,另一个一般情况下较慢,但是更加容易预测。为了两全其美(性能和可预测性)你可以在同一时间调用两个系统并等着谁先完成。通常这会是第一个系统,但是在进度变得缓慢时,第二个系统就可以在可接受的时间内完成:
CompletableFuture&String&
fast = fetchFast();
CompletableFuture&String&
predictable = fetchPredictably();
fast.acceptEither(predictable,
&&&&System.out.println(&Result:
s代表了从fetchFast()或是fetchPredictably()得到的String。我们不必知道也无需关心。
完整地转换第一个系统
applyToEither()算是 acceptEither()的前辈了。当两个futures快要完成时,后者只是简单地调用一些代码片段,applyToEither()将会返回一个新的future。当这两个最初的futures完成时,新的future也会完成。API有点类似于(…Async 变量也是可用的):
CompletableFuture&U& applyToEither(CompletableFuture&? extends
T& other, Function&? super
这个额外的fn功能在第一个future被调用时能完成。我不确定这个专业化方法的目的是什么,毕竟一个人可以简单地使用:fast.applyToEither(predictable).thenApply(fn)。因为我们坚持用这个API,但我们的确不需要额外功能的应用程序,我会简单地使用Function.identity()占位符:
CompletableFuture&String&
fast = fetchFast();
CompletableFuture&String&
predictable = fetchPredictably();
CompletableFuture&String&
firstDone =
&&&&fast.applyToEither(predictable,
Function.&String&identity());
第一个完成的future可以通过运行。请注意,从客户的角度来看,两个futures实际上是在firstDone的后面而隐藏的。客户端只是等待着future来完成并且通过applyToEither()使得当最先的两个任务完成时通知客户端。
多种结合的CompletableFuture
我们现在知道如何等待两个future来完成(使用thenCombine())并第一个完成(applyToEither())。但它可以扩展到任意数量的futures吗?的确,使用static辅助方法:
CompletableFuture&Void& allOf(CompletableFuture&?&... cfs)
CompletableFuture&Object& anyOf(CompletableFuture&?&... cfs)
allOf()当所有的潜在futures完成时,使用了一个futures数组并且返回一个future(等待所有的障碍)。另一方面anyOf()将会等待最快的潜在futures,请看一下返回futures的一般类型,这不是你所期望的吗?我们会在接下来的文章中关注一下这个问题。
我们探索了整个CompletableFuture API。我确信这样就能战无不胜了,所以在下一篇文章中我们将研究另一个简单的web爬虫程序的实现,使用CompletableFuture方法和Java 8 lambda表达式,我们也会看看CompletableFuture的缺点和不足。
原文链接:&&翻译:&-&
译文链接:&
[&转载请保留原文出处、译者和译文链接。]
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:99505次
积分:1282
积分:1282
排名:千里之外
转载:133篇
(1)(1)(3)(5)(7)(2)(5)(2)(12)(10)(14)(19)(1)(6)(7)(1)(12)(8)(3)(3)(3)(3)(15)

我要回帖

更多关于 complecate 的文章

 

随机推荐