java stream

collect

group + toMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Info {
final String key;
final String value;

Info(String key, String value) {
this.key = key;
this.value = value;
}

public String getKey() {
return key;
}

public String getValue() {
return value;
}

@Override
public String toString() {
return "Info{" +
"key='" + key + '\'' +
", value='" + value + '\'' +
'}';
}
}

List<Info> infos = new ArrayList<>();
infos.add(new Info("A", "B"));
infos.add(new Info("C", "D"));
Map<String, String> mp = infos.stream().collect(Collectors.toMap(Info::getKey, Info::getValue));
System.out.println(mp);

// 以 Info 的 key 值进行分组
Map<Info, Map<String, String>> data = new HashMap<>();
data.put(new Info("A", "A"), new HashMap<>());
data.put(new Info("A", "B"), new HashMap<>());
data.put(new Info("A", "C"), new HashMap<>());
data.put(new Info("B", "A"), new HashMap<>());
data.put(new Info("B", "B"), new HashMap<>());
data.put(new Info("B", "C"), new HashMap<>());
Map<String, Map<Info, Map<String, String>>> rd = data.entrySet()
.stream()
.collect(Collectors.groupingBy(kv -> kv.getKey().getKey(),
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
System.out.println(rd);

// output
{A=B, C=D}
{A={Info{key='A', value='B'}={}, Info{key='A', value='A'}={}, Info{key='A', value='C'}={}}, B={Info{key='B', value='A'}={}, Info{key='B', value='B'}={}, Info{key='B', value='C'}={}}}

group + mapping

1
2
3
4
5
6
7
8
9
10
11
12
13
// 以 Pair.left group,并合并 Pair.right
List<Pair<String, Map<String, String>>> data = new ArrayList<>();
data.add(Pair.of("A", new HashMap<String, String>(){{put("B", "C");}}));
data.add(Pair.of("D", new HashMap<String, String>(){{put("E", "F");}}));
Map<String, Map<String, String>> r = data.stream().collect(Collectors.groupingBy(Pair::getLeft, Collectors.mapping(Pair::getRight,
Collector.of(HashMap::new, Map::putAll, (x, y) -> {
x.putAll(y);
return x;
}))));
System.out.println(r);

// output
{A={B=C}, D={E=F}}

makefile 使用

格式

1
2
3
4
target ... : prerequisites ...
command
...
...

这里 target 无实际意义,类似于定义 scrope / function。

使用

PHONY

定义虚假文件,避免和时间文件冲突,同时可以解决 make: Nothing to be done for *** 的异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜  mf touch test       # 创建空白文件 test
➜ mf cat Makefile # 打印 Makefile
test:
ls
➜ mf make test # 执行 test 目标
make: 'test' is up to date. # Makefile 的 target 默认为文件,无法对应 Makefile 中定义的 test target
➜ mf vim Makefile # 添加 .PHONY: test
➜ mf cat Makefile # 打印 Makefile
.PHONY: test
test:
ls
➜ mf make test # 重新执行 target test
ls
Makefile test

定义变量

Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
VA := "I'm VA"        # 简单赋值
VB = "I'm VB" # 递归赋值
VC ?= "I'm VC" # 条件赋值,如果已定义则忽略
VC ?= "I,m VC Again" # 测试条件赋值
VA += "I'm VA's Tail" # 追加赋值,以空格
VD := "I'm VD"
VE = "I'm VE AND "$(VD) # 测试递归赋值
VD = "I'm VDD"

.PHONY: test

test:
$(eval VF="I'm VF") # 在 target 里面,这样定义变量
@echo $(VA)
@echo $(VB)
@echo $(VC)
@echo $(VD)
@echo $(VE)
@echo $(VF)

执行

1
2
3
4
5
6
7
$ make
I'm VA I'm VA's Tail
I'm VB
I'm VC
I'm VDD
I'm VE AND I'm VDD
I'm VF

不打印执行命令

Makefile

1
2
3
4
# 以 @ 开头

tgt:
@echo "HELLO"

输出

1
2
$ make
HELLO

读取文本内容

1
TAG := $(shell cat $(name)/VERSION)

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
DEPS_DIR:=../../../../deps
LIB_DIR:=$(DEPS_DIR)/lib
BIN_DIR:=$(DEPS_DIR)/bin
INCLUDE_DIR:=$(DEPS_DIR)/include
PATH:=$(DEPS_DIR)/bin:$(PATH)
LD_LIBRARY_PATH=$(LIB_DIR)
LIBRARY_PATH=$(LIB_DIR)
DYLD_LIBRARY_PATH=$(LIB_DIR)
CXXFLAGS=-std=c++11 -L $(LIB_DIR) -I $(INCLUDE_DIR) -Wno-unused-command-line-argument

FBF=parser lexer # bison & flex source files
PJF=driver main # for main programs
FILES=$(addsuffix .cpp, $(PJF)) # source files
OPF=$(addsuffix .o, $(PJF)) # main programs output
FBO=$(addsuffix .o, $(FBF)) # parser & lexer output file
EXE=wc

.PHONY: wc

all: wc

wc: $(FILES)
@export PATH=$(PATH) && \
export DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH) && \
export LIBRARY_PATH=$(LIBRARY_PATH) && \
$(MAKE) $(FBF) && \
$(MAKE) $(OPF) && \
$(CXX) $(CXXFLAGS) -o $(EXE) $(OPF) $(FBO) && \
echo "[PROCESS] compile wc done"


parser: parser.yy
@export PATH=$(PATH) && \
export DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH) && \
export LIBRARY_PATH=$(LIBRARY_PATH) && \
bison -v -d $< && \
$(CXX) $(CXXFLAGS) -c -o parser.o parser.tab.cc && \
echo "[PROCESS] compile parser done"

lexer: lexer.l
@export PATH=$(PATH) && \
export DYLD_LIBRARY_PATH=$(DYLD_LIBRARY_PATH) && \
export LIBRARY_PATH=$(LIBRARY_PATH) && \
flex -V && \
flex --outfile=lexer.yy.cc $< && \
$(CXX) $(CXXFLAGS) -c -o lexer.o lexer.yy.cc && \
echo "[PROCESS] compile lexer done"

clean:
@rm parser.tab.* parser.output location.hh position.hh stack.hh lexer.yy.cc \
$(FBO) $(OPF) $(EXE)

test:
@wc wc.in%

使用

1
2
3
$ make       # make all
$ make test # test
$ make clean # clean

设置并行度

1
2
# /etc/profile
export MAKEFLAGS="-j15 -l15" # 修改 15, 根据实际 cpu 核数设置

参考

如何组织一篇文章

解释性文章

  • 意图(Intent)
  • 动机(Motivation)
  • 适用性(Applicability)
  • 结构(Structure)
  • 参与者(Participants)
  • 协作(Collaborations)
  • 结果(Consequences)
  • 实现 / 范例(Implementation / Sample)
  • 已知应用(Known Uses)
  • 相关(Related)

开发文档

  • 背景
  • 现状
  • 方案设计
    • 架构
  • 详细设计
    • 协议
    • 技术细节
  • 部署方案
  • 实验方案
  • 项目排期

设计模式(二)之观察者模式

定义

主题 + 订阅者 = 观察者模式。观察者模式 定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

松耦合的威力

当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者之间松耦合。松耦合的设计之所以能让 OO 系统更有弹性,能够应对变化,是因为对象之间的互相依赖降到了最低。

为了交互对象之间的松耦合设计而努力。

气象监测应用

我们需要设计一个气象监测应用,为多种布告板提供数据支持,原始数据从气象站获取。我们可以假定使用 WeatherData 类实现我们的功能,该类有三个 getter 方法,可以从气象站获取数据,这三个 getter 方法如下。

1
2
3
float getTemperatuer();		// 获取温度
float getHumidity(); // 获取湿度
float getPressure(); // 获取气压

当气象站更新数据时,会调用 measurementsChanged() 方法。我们实现三个使用天气数据的,目前状况、气象统计、天气预报,一但 WeatherData 有数据更新,这些布告板必须马上更新。且,此系统必须可扩展,方便后续定制其他布告板。

类图设计如下。

maven 使用

[toc]

命令

1
2
3
4
5
6
7
8
9
10
11
12
13
# 清理
$ mvn clean

# 打包
$ mvn package

# 依赖树
$ mvn dependency:tree

# 指定 pom
$ mvn --settings YourOwnSettings.xml package
# OR
$ mvn -s YourOwnSettings.xml package

创建项目

1
$ mvn archetype:generate

打包

1
$ mvn package

指定模块

1
$ mvn clean package -pl <group-id>:<artifact-id> -am

指定 pom 文件

1
$ mvn package -f /path/to/pom.xml

可执行jar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<build>
<resources>
<resource>
<directory>
${project.basedir}/src/main/resources
</directory>
<filtering>true</filtering>
</resource>
</resources>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>attached</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>top.szhkai.mitest.TestZKFacade</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

搜索依赖包

1
$ mvn dependency:tree | grep recommend-service-common

指定编译版本

1
2
3
4
5
6
7
8
9
10
11
12
<plugins>  
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
...
</plugins>

配置多源文件

resources

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>

<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.java</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>

<resource>
<directory>src/main/generated</directory>
<includes>
<include>**/*.java</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>

sourceDirectory

1
<generatedSourcesDirectory>src/main/generated</generatedSourcesDirectory>

maven-compiler-plugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<build>
<sourceDirectory>.</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<includes>
<include>src/main/java/**/*.java</include>
<include>src/main/scala/**/*.scala</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>

build-helper-maven-plugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/generated</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

添加本地依赖

如果有需要添加一个 jar 包到工程,且没有可用的如 nexus 的包管理服务,可以参考这里

首先在工程创建一个目录 libs

1
$ mkdir libs

然后,安装 jar 包到 libs

1
2
3
4
5
6
7
$ mvn org.apache.maven.plugins:maven-install-plugin:2.5.2:install-file \
-Dfile=<path-to-jar> \
-DgroupId=<group-id> \
-DartifactId=<artifact-id> \
-Dversion=<version> \
-Dpackaging=jar \
-DlocalRepositoryPath=libs

然后,修改 pom。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<repositories>
<repository>
<id>project-repo</id>
<url>file://${project.basedir}/libs</url>
</repository>
</repositories>

<dependencies>
<dependency>
<groupId>org.apache.bahir</groupId>
<artifactId>flink-connector-kudu_${scala.version}</artifactId>
<version>1.2-SNAPSHORT</version>
</dependency>
</dependencies>