ARTS 第1周

ARTS (第1周)

ARTS 高效学习是左耳听风发起的一个高效学习方法,一个需要持续坚持的方法。ARTS 包含四块的内容(作者原话):

  • Algorithm。主要是为了编程训练和学习。每周至少做一个 leetcode 的算法题(先从Easy开始,然后再Medium,最后才Hard)。进行编程训练,如果不训练你看再多的算法书,你依然不会做算法题,看完书后,你需要训练。关于做Leetcode的的优势,你可以看一下我在coolshell上的文章 Leetcode 编程训练 - 酷 壳 - CoolShell(一个小时以内);
  • Review:主要是为了学习英文,如果你的英文不行,你基本上无缘技术高手。所以,需要你阅读并点评至少一篇英文技术文章,我个人最喜欢去的地方是 Medium(需要梯子,其他的可以社区的官方文档以及论文学习)以及各个公司的技术blog,如Netflix的(30min);
  • Tip:主要是为了总结和归纳你在是常工作中所遇到的知识点。学习至少一个技术技巧。你在工作中遇到的问题,踩过的坑,学习的点滴知识(也可以学习【极客时间】上的实用课程);
  • Share:主要是为了建立你的影响力,能够输出价值观。分享一篇有观点和思考的技术文章,也可以是技术总结的文章。

Algorithm 算法

LeetCode 三数之和

https://leetcode-cn.com/problems/3sum/submissions/

题目描述

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

思路:

关于这题,我最首先的做法是最繁杂的做法,先找出所有符合的数组,然后在添加到结果集合的时候判断是否重复(使用集合的container方法),进行去重。结果做成了四重循环的模式(因为container方法里就是一个循环,加上外层的三重循环,合起来四重),效率极低,上传到LeetCode,就直接返回说答案超时。

随后进行了修改,先排序,然后每个循环开始的时候,进行判断,判断当前是否和前一个数字相同。相同的话就跳过,在判断的同时进行排序,大大减少了去重的开销。但结果依然是超时,如下图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public List<List<Integer>> threeSum3(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> lists = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i++) {
if(nums[i] > 0)//去除不必要的计算
break;
if (i == 0 || nums[i] != nums[i - 1]) // 去除重复
for (int j = i + 1; j < nums.length - 1; j++) {
if (j == i + 1 || nums[j] != nums[j - 1]) // 去除重复
for (int k = j + 1; k < nums.length; k++) {
if (nums[i] + nums[j] + nums[k] == 0) {
ArrayList<Integer> integers = new ArrayList<>();
integers.add(nums[i]);
integers.add(nums[j]);
integers.add(nums[k]);
lists.add(integers);
break;
}
}
}
}
return lists;
}

在这个时候,我隐隐感觉到了这个题目,如果循环还是这么多重,多半答案都是超时。

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
public static List<List<Integer>> threeSum5(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
int target = 0;
for (int i = 0; i < nums.length; i++) {
if(i > 0 && (nums[i-1] == nums[i])){ //当前数据和前一个相同,则跳过
continue;
}
if(nums[i] > target){ //当前大于目标数,直接返回
break;
}
for (int j=i+1,k=nums.length-1; j<k;){
//去重
if(j > i+1 && nums[j-1]==nums[j]){
j++;
continue;
}
//去重
if(k < nums.length-1 && nums[k] == nums[k+1]){
k--;
continue;
}

int sum = nums[i] + nums[j] + nums[k];
if(sum > target){
k--;
}else if(sum < target){
j++;
}else {
List<Integer> item = new ArrayList<>();
item.add(nums[i]);
item.add(nums[j]);
item.add(nums[k]);
list.add(item);
j++;k--;
}
}
}
return list;
}

这边的思路就是先排序,从小到大,并且这题的要求是3数之和是0 因此要么三个数值都是0,要么必定有一个数字小于0, 因此,在从小到大排序的时候,如果第一个数字大于0,就可以结束了。

至于内层循环的判断,首先也是重复数字的跳过判断,然后则是从数组的最左端(最小数)和最右段(最大数)的坐标开始进行双端同时进行查找,将剩余数组里的最大数和最小数与外层循环的数字进行相加,若结果大于0,则集合的最大数字的坐标进行减一位的操作(减小),若大于0,则进行最左边左边的扩大一位的操作。等于0,则符合条件。

Review 英文文章

nginx 1.8.0 的文档

https://www.nginx.com/blog/nginx-unit-1-8-0-now-available/

该文档讲述了nginx 1.8.0的各类配置的使用

Tip 技巧

1、本周学会了hexo+GitHub搭建博客的技巧 也就是当前的博客。

参考链接https://www.cnblogs.com/liuxianan/p/build-blog-website-by-hexo-github.html

2、本周学会了spring boot方面的知识:

以下内容学习自尚硅谷

http://www.atguigu.com/

(1)yml语句的语法
1、基础语法

k:(空格)v:表示一对键值对(空格必须有);

空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的

1
2
server:
port: 8081

ps:属性和值也是大小写敏感;

2、值的写法

字面量:普通的值(数字,字符串,布尔)

​ k: v:字面直接来写;

​ 字符串默认不用加上单引号或者双引号;

​ “”:双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思

​ name: “zhangsan \n lisi”:输出;zhangsan 换行 lisi

​ ‘’:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据

​ name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi

3、对象、Map(属性和值)(键值对):

​ k: v:在下一行来写对象的属性和值的关系;注意缩进

​ 对象还是k: v的方式

1
2
3
user:
lastName: test
age: 18

行内写法:

1
user: {lastName: test,age: 18}
4、数组(List、Set):

用- 值表示数组中的一个元素

1
2
3
users:
- toms
- mike

行内写法

1
users: [toms,mike]
(2)多Profile文件

我们在主配置文件编写的时候,可以配置多Profile,方便不同环境下的修改、测试、运行

spring 默认使用application.properties的配置;

例如:如果使用profile,文件名可以是 application-dev.properties/yml (格式为application-{profile}.properties/yml),在配置文件中指定 spring.profiles.active=dev,那么就可以指定使用 application-dev.properties/yml 的配置

(3)配置文件的优先级

配置文件在spring boot里 分为jar包内 和jar包外的配置

在指定了外部配置的情况下,外部配置的优先级大于内部。

在jar包内的配置文件如下

spring 默认使用application.properties/yml 为文件名的配置;

优先级由高到底,高优先级的配置会覆盖低优先级的配置;

优先级路径如下:

–file:./config/

–file:./

–classpath:/config/

–classpath:/

spring boot还提供外部引入配置

例如在命令行参数里指定端口:java -jar xxx.jar –server.port=8087

官方文档的配置外部文件优先级(这里就不一一细说了)

参考链接:https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/htmlsingle/#boot-features-external-config

  1. Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).
  2. @TestPropertySource annotations on your tests.
  3. @SpringBootTest#properties annotation attribute on your tests.
  4. Command line arguments.
  5. Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property)
  6. ServletConfig init parameters.
  7. ServletContext init parameters.
  8. JNDI attributes from java:comp/env.
  9. Java System properties (System.getProperties()).
  10. OS environment variables.
  11. A RandomValuePropertySource that only has properties in random.*.
  12. Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants)
  13. Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants)
  14. Application properties outside of your packaged jar (application.properties and YAML variants).
  15. Application properties packaged inside your jar (application.properties and YAML variants).
  16. @PropertySource annotations on your @Configuration classes.
  17. Default properties (specified using SpringApplication.setDefaultProperties).
(4)Spring boot 的自动装配原理初识

部分注解

1
2
3
4
5
6
7
8
9
@ConfigurationProperties(prefix = "xxxxx") //将配置文件中配置的每一个属性的值,映射到这个组件中 例如类person(属性有name,age) 引入的prefix 为self.person 配置文件有self.person.name: tom ,self.person.age:18 ,那么就会在spring容器里注册进一个person的bean 其name属性为tom,age属性为18

@PropertySource(value = {"classpath:xxx.properties"})//额外引入xxx.properties

@ImportResource(locations = {"classpath:xxx.xml"})//引入spring配置的XML文件(既然使用了 spring boot 那么 个人就不推荐继续使用spring原有的xml方式进行配置)

@Import//导入指定的类 并将该类注册到spring容器中

@Conditional//条件注解 以次引申出了各类条件注解 例如@ConditionalOnBean可以判断当前环境中是否有指定的bean 、@ConditionalOnMissingBean判断当前环境是否没有指定的bean 等等各类注解。

SpringFactoriesLoader会扫描所有jar包类路径下 META-INF/spring.factories
把扫描到的这些文件的内容包装成properties对象
从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中。并且这些类一般都以xxxxAutoConfiguration命名。

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration//表示这是一个配置类
@ConditionalOnClass(RedisOperations.class)//判断当前环境里是否有指定的类
@EnableConfigurationProperties(RedisProperties.class)//启动指定类的ConfigurationProperties功能(将propertie的属性配置进bean里),并将其加入spring容器
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })//注册指定的类,功能与spring的xml文件中的<import />类似
public class JedisAutoConfiguration {

...

@Bean
@ConditionalOnMissingBean(RedisClient.class)//容器中如果没有RedisClient这个类,那么自动配置这个RedisClient
public RedisClient redisClient(@Qualifier("jedisPool")JedisPool pool) {
RedisClient redisClient = new RedisClient();
redisClient.setJedisPool(pool);
return redisClient;
}

...
}

上面是一个redis的自动配置类,首先redis自动配置类 会进行环境的判断判断当前环境中是否有自己需要的类,如果有则其注解@conConfiguration生效,表明这是一个配置类,随后该类将会根据配置在容器中添加配置类RedisProperties,同时也会注册LettuceConnectionConfiguration、JedisConnectionConfiguration2个类。

随后会开始下一步的具体的bean配置 该方法会进入redisClient()方法,该方法会先判断环境中是否有RedisClient类的bean,若有(例如:用户自定义了该bean的情况),则不会继续注册这个bean,若没有该指定的bean,则会进行注册。

(5)利用自动配置进行组件的切换

SpringBoot底层是Spring框架,Spring框架默认日志框架是用JCL,而spring boot的默认的日志配置则是slf4j+logback。

如果我们需要修改slf4j+log4j的方式,则仅需要去除slf4j+log4j的依赖,然后引入slf4j-log4j12的依赖即可。

因为spring boot有自动装配的功能,会自动判断环境,进行指定组件的注册,并不需要程序员进行太多的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
<exclusion>
<artifactId>log4j-over-slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>

PS:其它的组件切换也类似:例如tomcat切换成netty ,只需要将tomcat的依赖去掉,并加上netty 的依赖,即完成操作。

Share 分享

适时的总结,可以帮助我们梳理知识。

在我写完这篇总结的过程中,我发现了有几个隐藏的知识点未曾注意到,也发现了几个知识点还未理解透彻,这也可以帮助我们发现自己的不足。

做事情不要浮躁、每一个成功的人都是一点一滴积累起来的。将自己积累起来了,未来自己的选择也将更多更好。

有好的想法就去尝试。任何事情如果都是光想不做,结果都是会失败。成功都是一点一点的积累起来。