Stream 用来处理集合数据的,通过 stream 操作可以实现 SQL 的拥有的大部分查询功能

Java8 API 官方文档

下面借助例子,演示 stream 操作

Java userList 列表

private List<User> userList = Arrays.asList(
new User(101, "小明", 10, "男", "青海省", "西宁市"),
new User(102, "小青", 12, "女", "宁夏回族自治区", "银川市"),
new User(103, "小海", 8, "男", "西藏自治区", "拉萨市"),
new User(108, "阿刁", 18, "女", "西藏自治区", "拉萨市"),
new User(104, "小阳", 9, "女", "新疆维吾尔自治区", "乌鲁木齐市"),
new User(105, "小强", 14, "男", "陕西省", "西安市"),
new User(106, "小帅", 15, "男", "河北省", "石家庄市"),
new User(107, "小云", 15, "女", "河北省", "石家庄市")
);

MySQL user 表数据

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) PRIMARY KEY,
`name` varchar(20),
`age` int(2),
`gender` varchar(10),
`province` varchar(100),
`city` varchar(100)
) ; INSERT INTO `user` VALUES (101, '小明', 10, '男', '青海省', '西宁市');
INSERT INTO `user` VALUES (102, '小青', 12, '女', '宁夏回族自治区', '银川市');
INSERT INTO `user` VALUES (103, '小海', 8, '男', '西藏自治区', '拉萨市');
INSERT INTO `user` VALUES (104, '小阳', 9, '女', '新疆维吾尔自治区', '乌鲁木齐市');
INSERT INTO `user` VALUES (105, '小强', 14, '男', '陕西省', '西安市');
INSERT INTO `user` VALUES (106, '小帅', 15, '男', '河北省', '石家庄市');
INSERT INTO `user` VALUES (107, '小云', 15, '女', '河北省', '石家庄市');

查询字段 select - map

// select id from user
userList.stream()
.map(e -> e.getId())
.forEach(System.out::println);

至于如何实现 select id, name from user 查询多字段在下面 collector 收集器会详细讲解

条件 where - filter

// select * from user where age<10
userList.stream()
.filter(e-> e.getAge() < 10)
.forEach(System.out::println); // select * from user where age<10 and gender='男'
userList.stream()
.filter(e->e.getAge() < 10)
.filter(e->e.getGender()=="男")
.forEach(System.out::println);

最值、总和、数量、均值(max, min, sum, count, average)

// select max(age), min(age), sum(age), count(age), avg(age) from user
// max
Optional<Integer> maxAge = userList.stream()
.map(e -> e.getAge())
.max(Comparator.comparingInt(x -> x));
// 等同于
// Optional<Integer> maxAge = userList.stream()
// .map(e -> e.getAge())
// .max((x, y) -> x-y); // min
Optional<Integer> minAge = userList.stream()
.map(e -> e.getAge())
.min(Comparator.comparingInt(x -> x));
// sum
Optional<Integer> sumAge = userList.stream()
.map(e -> e.getAge())
.reduce((e, u) -> e + u);
// count
long count = userList.stream()
.map(e -> e.getAge())
.count();
// 平均值=总和/数量

排序 order by - sorted

// select * from user order by age
userList.stream()
.sorted(Comparator.comparingInt(User::getAge))
.forEach(System.out::println);

分页 limit - skip、limit

// select * from user limit 5
userList.stream()
.limit(5)
.forEach(System.out::println); // select * from user limit 5, 5
userList.stream()
.skip(5)
.limit(5)
.forEach(System.out::println); // select * from user order by age limit 1
userList.stream()
.sorted(Comparator.comparingInt(User::getAge))
.limit(1)
.forEach(System.out::println);
// 或者
Optional<User> minAgeUser = userList.stream()
.sorted(Comparator.comparingInt(User::getAge))
.findFirst();

是否存在 exists - anymatch

// select exists(select * from user where name='小海')
// 有没有名字叫“小海”的用户
boolean exists0 = userList.stream()
.anyMatch(e -> e.getName().equals("小海")); // select not exists(select * from user where name='小海')
// 是不是没有名字叫“小海”的用户
boolean exists1 = userList.stream()
.noneMatch(e -> e.getName().equals("小海")); // 是不是所有用户年龄都小于10岁
boolean exists2 = userList.stream()
.allMatch(e -> e.getAge() < 10);

收集操作 collect

收集操作就是遍历 stream 中的元素,并进行累加处理,即归约 reduction

归约的定义:

A reduction operation (also called a fold) takes a sequence of input elements and combines them into a single summary result by repeated application of a combining operation, such as finding the sum or maximum of a set of numbers, or accumulating elements into a list.

前面提到的 max() min() count() reduce() 都属于 reduction operation

但 collect() 又和前面这几种归约操作有所区别,它是 Mutable reduction 动态归约

动态归约的定义:

A mutable reduction operation accumulates input elements into a mutable result container, such as a Collection or StringBuilder, as it processes the elements in the stream

区别:动态归约将结果放进 Collection StringBuilder 这样的动态容器中,所以称为动态归约。

Stream 接口提供了两个 collect() 方法

<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner); <R, A> R collect(Collector<? super T, A, R> collector);

我们只需理解了第一个方法,第二个方法就手到擒来了

理解第一个 collect 方法,强烈建议阅读文档 动态归约的定义,下面只简单的介绍一下它

三个参数:

  • 供给者 supplier:负责提供动态容器,例如 Collectors、StringBuilder
  • 累加器 accumulator:负责将流中的元素做累加处理
  • 合并者 combiner :负责将两个容器的元素合并在一起

在串行流中,combiner 根本没有执行,所以随便写点啥满足参数对象就行。

如果说串行流是单线程,那么并行流就是多线程了

举个例子:


ArrayList<String> strings = new ArrayList<>();
for (T element : stream) {
strings.add(element.toString());
}
// 等同于
ArrayList<String> strings = stream.collect(() -> new ArrayList<>(),
(c, e) -> c.add(e.toString()),
(c1, c2) -> c1.addAll(c2));

与其传递三个参数这么麻烦,还不如直接传递一个对象呢!

这就是第二个 collect() 方法的由来,使用收集器 Collector 来替代三个参数

实际上,我们一般不需要自己创建 Collector 对象,Java8 提供了一个 Collectors 类,专门提供收集器 Collector 对象。毕竟我们平时能够使用到的收集操作也就那几种:转为集合对象、分组、统计。

下面以例子演示


在初看 stream 操作的时候,我被什么创建、中间操作、终止操作、不会改变原对象给弄晕了,我根本不关心这些,我的第一想法是怎么将操作后的数据导出来,重新变成集合对象。

toCollection

不使用收集器的情况下:

List<User> subUserList1 = userList.stream()
.filter(e -> e.getAge() < 10)
.filter(e -> e.getGender() == "男")
.collect(() -> new ArrayList<>(),
(c, e) -> c.add(e),
(c1, c2) -> c1.addAll(c2));

在 collect() 方法第二个参数累加器 accumulator (c, e) -> c.add(e) 这里,对流中元素进行了遍历,所以可以把流中元素添加到任意的集合容器中,List、Set、Map 等等

使用 Collectors 工具类提供的收集器:

// toList()
List<User> list = userList.stream()
.filter(e -> e.getAge() < 10)
.filter(e -> e.getGender() == "男")
.collect(Collectors.toList()); // toSet()
Set<User> set = userList.stream()
.filter(e -> e.getAge() < 10)
.filter(e -> e.getGender() == "男")
.collect(Collectors.toSet()); // toCollection(),想要返回什么容器,就 new 一个
ArrayList<User> collection = userList.stream()
.filter(e -> e.getAge() < 10)
.filter(e -> e.getGender() == "男")
.collect(Collectors.toCollection(
() -> new ArrayList<>()
));

这里插播一条新闻:如何将流转为数组?

Stream 提供了方法 toArray()

Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);

小试牛刀:

Object[] nameArray = userList.stream()
.map(e -> e.getName())
.toArray();
Arrays.stream(nameArray)
.forEach(System.out::println);
// 转为 User 对象数组
User[] users = userList.stream()
.filter(e -> e.getGender() == "女")
.toArray(User[]::new);
Arrays.stream(users)
.forEach(System.out::println);

toStringBuilder

不使用收集器的情况下:

StringBuilder joinName = userList.stream()
.map(e -> e.getName())
.collect(StringBuilder::new,
(s, e) -> s = s.length() > 0 ? s.append("-" + e) : s.append(e),
(s1, s2) -> s1.append(s2)
);

谁能告诉我在Java中怎么单独使用三元运算符?s = s.length() > 0 ? s.append("-" + e) : s.append(e) 我想把 s = 省略掉,但 Java 中不行

使用 Collectors 类提供的收集器:

String joinName1 = userList.stream()
.map(e -> e.getName())
.collect(Collectors.joining()); String joinName2 = userList.stream()
.map(e -> e.getName())
.collect(Collectors.joining("-")); String joinName3 = userList.stream()
.map(e -> e.getName())
.collect(Collectors.joining("-", "[", "]"));

至于 Collectors.joining() 参数分别代表什么含义,看一下它们的参数名称,就明白了

public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,	// 分隔符
CharSequence prefix, // 前缀
CharSequence suffix) // 后缀

toMap

在 Collectors 中一共有3个 toMap(),它们用来处理不同的问题

两个参数的 toMap

 Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}

参数 keyMapper 用来获取key;valueMapper 用来获取 value

它的内部调用了四个参数的 toMap() 方法

例子

Map<Integer, User> map1 = userList.stream()
.collect(Collectors.toMap(e -> e.getId(), Function.identity()));
System.out.println(map1);
// Function.identity() 等价于 e -> e // select id, name, gender from user
Map<Integer, Map<String, Object>> map2 = userList.stream()
.collect(Collectors.toMap(e -> e.getId(), e -> {
Map<String, Object> map = new HashMap<>();
map.put("gender", e.getGender());
map.put("name", e.getName());
map.put("id", e.getId());
return map;
}));
System.out.println(map2);

你:如果 key 冲突了咋办?

Java8:你想咋办就咋办

三个参数的 toMap

Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction) {
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}

第三个参数 mergeFunction 就是用来处理 key 键冲突的

内部也是调用了四个参数的 toMap() 方法

例子

// 如果 key 冲突,那么将冲突的 value 值拼接在一起
Map<String, String> map3 = userList.parallelStream()
.collect(Collectors.toMap(
e -> e.getGender(),
e -> e.getName(),
(o1, o2) -> o1 + ", " + o2
)
);
System.out.println(map3);

你:我想自己 new 一个 Map 对象

四个参数的 toMap

Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier)

参数 mapSupplier 用来提供返回容器

例子

LinkedHashMap<String, String> map4 = userList.parallelStream()
.collect(Collectors.toMap(e -> e.getGender(), e -> e.getName(), (o1, o2) -> o1 + ", " + o2, LinkedHashMap::new));
System.out.println(map4);

reducing

单参数和两参数的 reducing()

Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op)
Collector<T, ?, U> reducing(U identity, Function<? super T,? extends U> mapper, BinaryOperator<U> op)

以例子具体解释这两个方法

Optional<String> names1 = userList.stream()
.map(User::getName)
.collect(Collectors.reducing((e1, e2) -> e1 + "," + e2));
System.out.println(names1.get()); // 等同于
String names2 = userList.stream()
.collect(Collectors.reducing(
"", (e) -> e.getName(), (e1, e2) -> e1 + "," + e2)
);
System.out.println(names2);

输出结果:

小明,小青,小海,阿刁,小阳,小强,小帅,小云
,小明,小青,小海,阿刁,小阳,小强,小帅,小云

参数 identity 表示返回结果的初始值

三参数的 reducing()

reducing(U identity, Function<? super T,? extends U> mapper, BinaryOperator<U> op)

identity 是初始值,mapper 会对元素先进行一次处理,然后 op 对元素进行归约操作

注意: 返回类型要和参数 identity 的一致。

你也许会纳闷,为什么有的返回一个 Optional<String> 类型数据,而有的就返回了 String

因为含有参数 identity 的 reduing 方法中返回值有初始值,也就是 identity,所以不会出现空的情况

下面Collectors 提供的一些常用归约收集器

// minBy、maxBy
Optional<User> minAgeUser = userList.stream()
.collect(Collectors.minBy((o1, o2) -> o1.getAge() - o2.getAge())); // counting
Long count = userList.stream()
.collect(Collectors.counting()); // summingInt、summingLong、summingDouble、
Integer sumAge = userList.stream()
.collect(Collectors.summingInt(User::getAge)); // averagingInt、averagingLong、averagingDouble
// 平均值内部是总值/数量,所以返回值是浮点数 dobule
Double avgAge = userList.stream()
.collect(Collectors.averagingInt(User::getAge));

你也许觉得每次都要执行一遍 minBy、maxBy、counting、summingXxx、averagingXxx 这些太麻烦了,有没有一次执行就获取所有这些方法结果?

有的。这就是 summarizingXxx

Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper)
Collector<T, ?, LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper)
Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper)

这里不演示了,实际上你看一下 XxxSummaryStatistics 这些类就明白了,比如

public class IntSummaryStatistics implements IntConsumer {
private long count;
private long sum;
private int min = Integer.MAX_VALUE;
private int max = Integer.MIN_VALUE;
...
}

group by

最最激动人心的时候到了,我们要使用分组了!!!

Map<String, List<User>> map = userList.stream()
.collect(Collectors.groupingBy(User::getGender));

SQL 中的 group by 结果集中只能包含分组字段和聚合函数计算结果,这段代码比它更加全面

我们使用如下语句输出结果

map.keySet().stream()
.forEach((e) -> {
System.out.println(e + "=" + map.get(e));
});

显示结果:

女=[User{id=102, name='小青', age=12, gender='女', province='宁夏回族自治区', city='银川市'}, User{id=108, name='阿刁', age=18, gender='女', province='西藏自治区', city='拉萨市'}, User{id=104, name='小阳', age=9, gender='女', province='新疆维吾尔自治区', city='乌鲁木齐市'}, User{id=107, name='小云', age=15, gender='女', province='河北省', city='石家庄市'}]
男=[User{id=101, name='小明', age=10, gender='男', province='青海省', city='西宁市'}, User{id=103, name='小海', age=8, gender='男', province='西藏自治区', city='拉萨市'}, User{id=105, name='小强', age=14, gender='男', province='陕西省', city='西安市'}, User{id=106, name='小帅', age=15, gender='男', province='河北省', city='石家庄市'}]

它真的分组了!!这是真正的分组

那怎么对分组中的元素进行操作呢,像 SQL 那样??

完全不用担心,Collectors 提供了三个 groupBy 方法返回分组收集器

Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T,? extends K> classifier)

Collector<T, ?, Map<K, D>> groupingBy(Function<? super T,? extends K> classifier,
Collector<? super T,A,D> downstream) Collector<T, ?, M> groupingBy(Function<? super T,? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T,A,D> downstream)

让我们放飞想象的翅膀,思考一下这几个参数分别有什么用。

downstream ?有 down 就表示有 up。那么谁是 upstream,很明显是 userList.stream,那么 downstream 就是分组集合的流喽。猜测 downstream 收集器是对分组中的元素进行归约操作的,就像是分组 SQL 语句字段中的聚合操作一样。

// select gender, count(*) from user group by gender
Map<String, Long> map2 = userList.stream()
.collect(Collectors.groupingBy(User::getGender, Collectors.counting()));
System.out.println(map2);

输出结果确实不出所料!这就是证明参数 downstream 确实是分组集合元素的收集器。

Supplier<M> mapFactory 这函数式接口方法不会有参数传入,所以不会操作集合元素;它只是返回一个变量。同志们,注意观察三个方法返回值,前二者都指定了 Map 作为归约操作的返回类型,而第三个要我们自己定义,使用 mapFactory 提供返回的数据容器

两参数的 groupingBy 方法其实是调用了三参数的 groupingBy 方法(而单参数 groupingBy 调用了两参数的 groupingBy)

    Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream) {
return groupingBy(classifier, HashMap::new, downstream);
}

groupingBy 经常使用 Collectors.mapping() 处理分组集合

Map<String, List<Map<String, Object>>> map4 = userList.stream()
.collect(Collectors.groupingBy(
User::getGender,
Collectors.mapping(e -> {
Map<String, Object> m = new HashMap<>();
m.put("name", e.getName());
m.put("id", e.getId());
return m;
}, Collectors.toList())
));
System.out.println(map4);

输出结果:

{女=[{name=小青, id=102}, {name=阿刁, id=108}, {name=小阳, id=104}, {name=小云, id=107}], 男=[{name=小明, id=101}, {name=小海, id=103}, {name=小强, id=105}, {name=小帅, id=106}]}

partitionBy

实际上也是分组,只不过 partitionBy 是按照布尔值(真假)来分组

Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
} Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
Collector<? super T, A, D> downstream)

例子:大于10岁为一组,小于等于10的为一组

Map<Boolean, List<User>> map1 = userList.stream()
.collect(Collectors.partitioningBy(e -> e.getAge() > 10));

例子:统计大于10岁的有多少人,小于等于10岁的有多少人

Map<Boolean, Long> map2 = userList.stream()
.collect(Collectors.partitioningBy(e -> e.getAge() > 10, Collectors.counting()));

第二个参数 downstream 用来处理分组集合

结语

Java8 提供的 stream 几乎是穷尽了所有集合元素能有的操作,起码是穷尽了我脑海里对集合元素操作的所有想象

这篇文章也列举了 stream 绝大部分的功能,尽量写得通俗易懂,但读者理解起来可能还是有模糊的地方,这时建议大家参考 Java8 API 官方文档 ,多做几个 Demo 加深理解

不要过度使用

stream 是为了方便集合操作,简化代码而推出的,提升代码执行效率并不是它的目的。

虽然,并行流会对代码的执行效率有较大的提升(尤其是数据量非常大的时候),但也依赖于计算机的CPU配置。

Stream 能实现的功能,for 循环都能实现,只是 Stream 代码一般比较简洁,可读性强。但在某些情况下,使用 for 循环要比 Stream 要简洁代码逻辑清晰

举个例子:

	private List<Order> orderList = Arrays.asList(
new Order(103),
new Order(106),
new Order(107),
new Order(104),
new Order(102),
new Order(103),
new Order(102),
new Order(101),
new Order(104),
new Order(102),
new Order(105)
);
// 现根据 userId 设置 Order 对象的 name 属性 // 使用 stream
List<Order> newOrderList = orderList.stream()
.map(o -> userList.stream()
.filter(u -> u.getId() == o.getUserId())
.findFirst()
.map(u -> {
o.setUserName(u.getName());
return o;
})
.orElse(o))
.collect(Collectors.toList());
newOrderList.stream().forEach(System.out::println); // 使用 for 循环
for (Order o : orderList) {
for (User u : userList) {
if (o.getUserId() == u.getId()) {
o.setUserName(u.getName());
break;
}
}
}
orderList.stream().forEach(System.out::println);

在这个例子中,使用 for 循环要比 使用 stream 干净利落的多,代码逻辑清晰简明,可读性也比 stream 好。

Java8 新特性(二)- Stream的更多相关文章

  1. 乐字节-Java8新特性之Stream流(上)

    上一篇文章,小乐给大家介绍了<Java8新特性之方法引用>,下面接下来小乐将会给大家介绍Java8新特性之Stream,称之为流,本篇文章为上半部分. 1.什么是流? Java Se中对于 ...

  2. Java8 新特性之Stream API

    1. Stream 概述 Stream 是Java8中处理集合的关键抽象概念,可以对集合执行非常复杂的查找,过滤和映射数据等操作; 使用 Stream API 对集合数据进行操作,就类似于使用 SQL ...

  3. 【Java8新特性】Stream API有哪些中间操作?看完你也可以吊打面试官!!

    写在前面 在上一篇<[Java8新特性]面试官问我:Java8中创建Stream流有哪几种方式?>中,一名读者去面试被面试官暴虐!归根结底,那哥儿们还是对Java8的新特性不是很了解呀!那 ...

  4. 【Java8新特性】- Stream流

    Java8新特性 - Stream流的应用 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! ...

  5. Java8新特性之Stream

    原文链接:http://ifeve.com/stream/ Java8初体验(二)Stream语法详解 感谢同事[天锦]的投稿.投稿请联系 tengfei@ifeve.com上篇文章Java8初体验( ...

  6. Java8 新特性 Lambda & Stream API

    目录 Lambda & Stream API 1 Lambda表达式 1.1 为什么要使用lambda表达式 1.2 Lambda表达式语法 1.3 函数式接口 1.3.1 什么是函数式接口? ...

  7. Java8新特性--流(Stream)

    1.简介      Java 8是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和JVM等方面的十多个新特性.在本文中我们一起来学习引入的一个新特性- ...

  8. java8 新特性入门 stream/lambda

    Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利.高效的聚合操作(aggregate operation),或者大批量数据操作 (b ...

  9. 【Java8新特性】Stream(分类+案例)

    一.Stream概述 什么是Stream? Stream是Java8引入的全新概念,它用来处理集合中的数据,可以让你以一种声明的方式处理数据. Stream 使用一种类似用 SQL 语句从数据库查询数 ...

随机推荐

  1. PTA 6-2 多项式求值

    PTA 6-2 多项式求值 本题要求实现一个函数 本题要求实现一个函数,计算阶数为n,系数为a[0] ... a[n]的多项式f(x)=∑i=0n(a[i]×xi)" role=" ...

  2. Python 科学计算库numpy

    Numpy基础数据结构 NumPy数组是一个多维数组对象,称为ndarray.其由两部分组成: 实际的数据 描述这些数据的元数 # 多维数组ndarray import numpy as np ar ...

  3. P1034 台阶问题一

    题目描述 有 \(N\) 级的台阶,你一开始在底部,每次可以向上迈最多2级台阶(最少1级),问到达第 \(N\) 级台阶有多少种不同方式. 输入格式 一个正整数 \(N(\le 20)\) . 输出格 ...

  4. 深入理解CSS盒模型(转)

    转自:https://www.cnblogs.com/chengzp/p/cssbox.html 基本概念 盒模型的组成大家肯定都懂,由里向外content,padding,border,margin ...

  5. 【34.54%】【codeforces 675E】Trains and Statistic

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  6. ABP运行Login failed for user 'IIS APPPOOL XXXXX Reason: Could not find a login matching the name provided问题解决

    我们在ABP官网上面生成解决方案后,编译完成,将数据库连接字符串中的Ip改成自己的测试数据库Ip直接在Vs里面调试运行没有任何问题. 发布之后到文件夹后运行,就报如下图异常. VS里面可以跑起来,单独 ...

  7. vagrant在windows下的安装和配置(一)

    记录一下安装和配置过程中的一些坑步骤一分别下载vagrant和VirtualBox,我这里下载的是vagrant_1.9.1.msi 和 VirtualBox-5.1.14-112924-Win.ex ...

  8. 【一起学源码-微服务】Nexflix Eureka 源码八:EurekaClient注册表抓取 精妙设计分析!

    前言 前情回顾 上一讲 我们通过单元测试 来梳理了EurekaClient是如何注册到server端,以及server端接收到请求是如何处理的,这里最重要的关注点是注册表的一个数据结构:Concurr ...

  9. 【一起学源码-微服务】Nexflix Eureka 源码十:服务下线及实例摘除,一个client下线到底多久才会被其他实例感知?

    前言 前情回顾 上一讲我们讲了 client端向server端发送心跳检查,也是默认每30钟发送一次,server端接收后会更新注册表的一个时间戳属性,然后一次心跳(续约)也就完成了. 本讲目录 这一 ...

  10. phpqrcode生成任意尺寸的二维码

    在Thinkphp中整合phpqrcode用于生成二维码,其代码如下: vendor("phpqrcode.phpqrcode"); QRcode::png('http://www ...