给面试官讲解hashmap底层原理后,他表示很看好我

thbcm阅读(190)

作为一名程序员,你面试的时候肯定被问过HashMap这个知识点,它的基本实现原理是每个面试者都该掌握的,当我们熟练的掌握了HashMap 的内部实现原理。面对面试官的询问,就能应答自如,接下来小编将带大家了解 JDK7 版本的 HashMap基础及其实现原理。

一、 HashMap介绍

HashMap简介: HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。 HashMap 继承于AbstractMap,实现了MapCloneablejava.io.Serializable接口。 HashMap 的实现不是同步的,这意味着它不是线程安全的。它的keyvalue都可以为null。此外,HashMap中的映射不是有序的。 HashMap 的实例有两个参数影响其性能:“初始容量” 和 “加载因子”。容量 是哈希表中桶的数量,初始容量 只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行rehash操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。 通常,默认加载因子是 0.75, 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 getput 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。

HashMap的继承关系:

HashMap与Map关系如下图

HashMap的构造函数

HashMap共有4个构造函数,如下:

// 默认构造函数。
HashMap()
// 指定“容量大小”的构造函数
HashMap(int capacity)
// 指定“容量大小”和“加载因子”的构造函数
HashMap(int capacity, float loadFactor)
// 包含“子Map”的构造函数
HashMap(Map<? extends K, ? extends V> map)

二、JDK7 中 HashMap 底层原理

HashMapJDK7 或者 JDK8 中采用的基本存储结构都是数组+链表形式。本节主要是研究 HashMapJDK7 中的底层实现,其基本结构图如下所示:

上图中左边橙色区域是哈希表,右边蓝色区域为链表,链表中的元素类型为 Entry,它包含四个属性分别是:

  • K key
  • V value
  • int hash
  • Entry next

那么为什么会出现数组+链表形式的存储结构呢?这里简单地阐述一下,后续将以源码的形式详细介绍。 我们在使用 HashMap.put("Key", "Value")方法存储数据的时候,底层实际是将keyvalueEntry的形式存储到哈希表中,哈希表是一个数组,那么它是如何将一个 Entry 对象存储到数组中呢?是如何确定当前 keyvalue 组成的 Entry 该存到数组的哪个位置上,换句话说是如何确定 Entry 对象在数组中的索引的呢?通常情况下,我们在确定数组的时候,都是在数组中挨个存储数据,直到数组全满,然后考虑数组的扩容,而 HashMap 并不是这么操作的。在 Java 及大多数面向对象的编程语言中,每个对象都有一个整型变量 hashcode,这个 hashcode 是一个很重要的标识,它标识着不同的对象,有了这个 hashcode,那么就很容易确定 Entry 对象的下标索引了,在 Java 语言中,可以理解 hashcode 转化为数组下标是按照数组长度取模运算的,基本公式如下所示:

int index = HashCode(key) % Array.length

实际上,在 JDK 中哈希函数并没有直接采取取模运算,而是利用了位运算的方式来提高性能,在这里我们理解为简单的取模运算。 我们知道了对 Key 进行哈希运算然后对数组长度进行取模就可以得到当前 Entry 对象在数组中的下标,那么我们可以一直调用 HashMapput 方法持续存储数据到数组中。但是存在一种现象,那就是根据不同的 Key 计算出来的结果有可能会完全相同,这种现象叫作“哈希冲突”。既然出现了哈希冲突,那么发生冲突的这个数据该如何存储呢?哈希冲突其实是无法避免的一个事实,既然无法避免,那么就应该想办法来解决这个问题,目前常用的方法主要是两种,一种是开放寻址法,另外一种是链表法。 开放寻址法是原理比较简单,就是在数组里面“另谋高就”,尝试寻找下一个空档位置。而链表法则不是寻找下一个空档位置,而是继续在当前冲突的地方存储,与现有的数据组成链表,以链表的形式进行存储。HashMap 的存储形式是数组+链表就是采用的链表法来解决哈希冲突问题的。具体的详细说明请继续往下看。 在日常开发中,开发者对于 HashMap 使用的最多的就是它的构造方法、put 方法以及get 方法了,下面就开始详细地从这三个方法出发,深入理解HashMap 的实现原理。

三、HashMap put、get 方法流程图

这里提供一个 HashMapput 方法存储数据的流程图供读者参考:

这里提供一个 HashMapget 方法获取数据的流程图供读者参考:

上面中 get 流程图画得稍微比正常的要复杂一些,只是为了描述流程更加清晰。

四、常见的 HashMap 的迭代方式

在实际开发过程中,我们对于 HashMap 的迭代遍历也是常见的操作,HashMap 的迭代遍历常用方式有如下几种:

  • 方式一:迭代器模式
Map<String, String> map = new HashMap<>(16);
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<String, String> next = iterator.next();
    System.out.println(next.getKey() + ":" + next.getValue());
}

  • 方式二:遍历 Set>方式
Map<String, String> map = new HashMap<>(16);
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

  • 方式三:forEach 方式(JDK8 特性,lambda)
Map<String, String> map = new HashMap<>(16);
map.forEach((key, value) -> System.out.println(key + ":" + value));

  • 方式四:keySet 方式
Map<String, String> map = new HashMap<>(16);
Iterator<String> keyIterator = map.keySet().iterator();
while (keyIterator.hasNext()) {
    String key = keyIterator.next();
    System.out.println(key + ":" + map.get(key));
}

(推荐微课:Java微课

把这四种方式进行比较,前三种其实属于同一种,都是迭代器遍历方式,如果要同时使用到 keyvalue,推荐使用前三种方式,如果仅仅使用到 key,那么推荐使用第四种。

文章来源:www.toutiao.com/a6862688709423137294/

以上就是W3Cschool编程狮关于hashmap底层原理的相关介绍了,希望对大家有所帮助。

Array.apply(),new Array(),arr =[] 三者的区别

thbcm阅读(184)

为什么会写这篇文章呢?看Vue文档渲染函数的时候发现一个问题很好奇,Array.apply(null, { length: 20 })为什么这样定义数组?然后查阅资料做了一个小结记录一下,麻雀虽小,五脏俱全。

Array.apply()

apply()在MDN中解释是这样的:

func.apply(thisArg, [argsArray])

thisArg 必选的。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。

argsArray 可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象

function printArguments() {
    console.log(arguments)
    Array.prototype.forEach.call(arguments, function (item) {
        console.log(item);
    });
}
printArguments(undefined,undefined)

由此可见{length:20}是个类数组argument只提供了length的属性相当于创建了一个长度为20,每个元素为undefined的数组

(20) [undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined]

new Array()

new Array(20)Array(20)只是创建了一个长度为20,元素是空的数组

(20) [empty × 20]

arr = []

let arr=[];
arr.length= 20
(20) [empty × 20]

由此可见new Array(20)let arr=[];arr.length= 20等价

Array.from()

Array.from() 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。

Array.from({length:20})
(20) [undefined, undefined, undefined, undefined, undefined, undefined, undefi

文章来源:公众号–小丑的小屋 作者:小丑

以上就是W3Cschool编程狮关于Array.apply(),new Array(),arr =[] 三者的区别的相关介绍了,希望对大家有所帮助。

前端性能分析工具,Keepfast 发布新版本 v0.6.2

thbcm阅读(225)

Keepfast 是一个性能分析工具,能够分析网站的资源构建性能和页面性能,生成性能报告并提供优化建议,让性能监控更方便。

主要特性:

分析并生成构建性能报告,可直观的对比两个版本的构建性能,以便尽早发现构建性能问题

基于 Lighthouse 对网站进行性能测试,给出各方面性能指标和相关建议

预置 Gitee API 支持,能够在 Gitee 仓库中有新的提交时为其生成一个性能报告,随时跟踪性能指标和得分

Keepfast v0.6.2 发布了,Keepfast 目前已用在码云内部为前端项目提供性能监控支持。

相比上次发布的 v0.2.1 版本,有如下改动:

问题修复

  • 报告文件未正确加载 (d10ef63)
  • 表格数据高亮效果不正确 (074b72d)
  • ps aux 命令在 Windows 上无效 (2241531)
  • 上传错误未被正确捕获 (0bf4f06)
  • 报错时未正确设置进程退出码 (8ff6245)
  • 配置项无法使用 null 作为值 (7c5b77a)

新特性

  • 添加 upload.writer.performance 配置 (1e602bc)
  • 添加 lighthouse.locale 配置 (8473eca)
  • 设置 lighthouse 报告的默认语言为中文 (771f1ac)
  • 添加 –use-message 选项 (78776b9)
  • 添加构建耗时限制 (ef9d242)

以上就是W3Cschool编程狮关于前端性能分析工具,Keepfast 发布新版本 v0.6.2 的相关介绍了,希望对大家有所帮助。

硬核!一文带你深入了解SpringMVC

thbcm阅读(231)

一丶SpringMVC概述

  • Spring 为展现层提供的基于 MVC 设计理念的优秀的Web 框架,是目前最主流的 MVC 框架之一
  • Spring3.0 后全面超越 Struts2,成为最优秀的 MVC 框架
  • Spring MVC 通过一套 MVC 注解,让 POJO 成为处理请求的控制器,而无须实现任何接口。
  • 支持 REST 风格的 URL 请求
  • 采用了松散耦合可插拔组件结构,比其他 MVC 框架更具扩展性和灵活性

二丶SpringMVC简单使用

1)在 web.xml 中配置 DispatcherServlet:

<!-- 配置 DispatcherServlet -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置 DispatcherServlet 的一个初始化参数: 配置 SpringMVC 配置文件的位置和名称 -->
        <!-- 
            实际上也可以不通过 contextConfigLocation 来配置 SpringMVC 的配置文件, 而使用默认的.
            默认的配置文件为: /WEB-INF/<servlet-name>-servlet.xml
        -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

2)加入 Spring MVC 的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">


    <!-- 配置自定扫描的包 -->
    <context:component-scan base-package="cbuc.life.springmvc"></context:component-scan>


    <!-- 配置视图解析器: 如何把 handler 方法返回值解析为实际的物理视图 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>


</beans>

3)编写处理请求的处理器,并使用@Controller 注解标识为处理器

@Controller
public class HelloWorldController {
    /**
       1. 使用 @RequestMapping 注解来映射请求的 URL
       2. 返回值会通过视图解析器解析为实际的物理视图, 对于 InternalResourceViewResolver 视图解析器, 会做如下的解析:
          通过 prefix + returnVal + 后缀 这样的方式得到实际的物理视图, 然会做转发操作
          ==> /WEB-INF/views/success.jsp
     */
    @RequestMapping("/helloworld")
    public String hello(){
        System.out.println("hello world");
        return "success";
    }
}

4) 编写视图 JSP

在/WEB-INF/views/目录下创建一个succes.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1>成功跳转页面</h1>
</body>
</html>

5)将项目运行起来访问 :localhost:8080/hellowoorld

(推荐课程:Spring教程

三丶使用 @RequestMapping 映射请求

  • Spring MVC 使用 @RequestMapping 注解为控制器指定可以处理哪些 URL 请求
  • 在控制器的 定义及方法定义处都可标注
  • 类定义:提供初步的请求映射信息。相对于 WEB 应用的根目录
  • 方法:提供进一步的细分映射信息。相对于类定义处的 URL。若类定义处未标注 @RequestMapping,则方法处标记的 URL 相对于WEB 应用的根目录
  • DispatcherServlet 截获请求后,就通过控制器上@RequestMapping 提供的映射信息确定请求所对应的处理 方法。

1)标准请求头

2)@RequestMapping

@RequestMapping 的valuemethodparamsheads 分别表示请求 URL请求方法请求参数请求头的映射条件,他们之间是 的关系,联合使用多个条件可让请求映射更加精确化。

/**
     * 可以使用 params 和 headers 来更加精确的映射请求. params 和 headers 支持简单的表达式.
     * 
     * @return
     */
    @RequestMapping(value = "testParamsAndHeaders",
                    params = { "username","age!=10" },
                    headers = { "Accept-Language=en-US,zh;q=0.8" },
                    method = RequestMethod.POST)
    public String test() {
        System.out.println("test...");
        return "success";
    }

3)支持Ant 风格

  • ? :匹配文件名中的一个字符

/user/createUser?

匹配 /user/createUsera 或者 user/createUserb 等 URL

  • * :匹配文件名中的任意字符

/user/*/createUser

匹配 /user/aaa/createUser 或者 /user/bbb/createUser 等 URL

  • ** :匹配多层路径

/user/\/createUser**

匹配 /user/createUser 或者 /user/aaa/bbb/createUser 等 URL

四丶@PathVariable

映射 URL 绑定的占位符

  • 带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义
  • 通过@PathVariable可以将 URL 中占位符参数绑定到控制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过@PathVariable("xxx") 绑定到操作方法的入参中。
/**
 * @PathVariable 可以来映射 URL 中的占位符到目标方法的参数中.
 */
@RequestMapping("/testPathVariable/{id}")
public String test(@PathVariable("id") Integer id) {
    System.out.println("id: " + id);
    return "success";
}
复制代码

五丶REST风格

REST:即 Representational State Transfer。(资源)表现层状态转化。是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便, 所以正得到越来越多网站的采用

示例

  • /order/1 HTTP GET得到 id = 1 的 order 记录
  • /order/1 HTTP DELETE删除 id = 1的 order 记录
  • /order/1 HTTP PUT更新 id = 1的 order 记录
  • /order HTTP POST新增 一条order记录

六丶@RequestParam 绑定请求参数值

  • 在处理方法入参处使用 @RequestParam 可以把请求参数传递给请求方法
  • value:参数名
  • required:是否必须;默认为 true,表示请求参数中必须包含对应的参数,若不存在,将抛出异常
/**
 * @RequestParam 来映射请求参数. value 值即请求参数的参数名 required 该参数是否必须. 默认为 true
 *               defaultValue 请求参数的默认值
 */
@RequestMapping(value = "/testRequestParam")
public String testRequestParam(
        @RequestParam(value = "username") String username,
        @RequestParam(value = "age", required = false, defaultValue = "0") int age) {
    System.out.println("testRequestParam, username: " + username + ", age: " + age);
    return "success";
}

七丶@RequestHeader 绑定请求报头的属性值

/**
 *   映射请求头信息 用法同 @RequestParam
 */
@RequestMapping("/testRequestHeader")
public String testRequestHeader(
        @RequestHeader(value = "Accept-Language") String al) {
    System.out.println("testRequestHeader, Accept-Language: " + al);
    return "success";
}

八丶@CookieValue 绑定请求中的 Cookie 值

/**
 * @CookieValue: 映射一个 Cookie 值. 属性同 @RequestParam
 */
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {
    System.out.println("testCookieValue: sessionId: " + sessionId);
    return "success";
}

九丶POJO 对象绑定请求参数值

/**
 * Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配, 自动为该对象填充属性值。支持级联属性。
 * 如:dept.deptId、dept.address.tel 等
 */
@RequestMapping("/testPojo")
public String testPojo(User user) {
    System.out.println("testPojo: " + user);
    return "success";
}

十丶MVC 中Handler 方法可以接收的ServletAPI 类型的参数

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • Writer
  • java.security.Principal
  • Locale
  • InputStream
  • OutputStream
  • Reader

(推荐微课:Spring微课)

十一丶处理模型数据

1)ModelAndView

处理方法返回值类型为 ModelAndView时,方法体可通过该对象添加模型数据,ModelAndView中既包含视图信息,也包含模型数据信息

2)Map 及 Model

入参为 org.springframework.ui.Modelorg.springframework.ui.ModelMapjava.uti.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中。

3)@SessionAttributes:

将模型中的某个属性暂存到HttpSession中,以便多个请求之间可以共享这个属性(从session域中获取)

  • 若希望在多个请求之间共用某个模型属性数据,则可以在 控制器上标注一个 @SessionAttributes,Spring MVC 将在模型中对应的属性暂存到 HttpSession 中。
  • @SessionAttributes除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中

1)@SessionAttributes(types=User.class): 会将隐含模型中所有类型为 User.class 的属性添加到会话中

2)@SessionAttributes(value={“user1”, “user2”}):会将隐含模型中对象名为user1user2 的属性添加到会话中

3)@SessionAttributes(types={User.class, Dept.class}):会将隐含模型中所有类型为 User.classDept.class 的属性添加到会话中

4)@SessionAttributes(value={“user1”, “user2”}, types={Dept.class}):会将隐含模型中对象名为user1user2 的属性和所有类型为 Dept.class 的属性添加到会话中

4)@ModelAttribute

方法入参标注该注解后, 入参的对象就会放到数据模型中

十二丶@ModelAttribute

  • 在方法定义上使用 @ModelAttribute 注解:Spring MVC在调用目标处理方法前,会先逐个调用在方法级上标注了@ModelAttribute 的方法。
  • 在方法的入参前使用 @ModelAttribute 注解:
  • 可以从隐含对象中获取隐含的模型数据中获取对象,再将请求参数绑定到对象中,再传入入参
  • 将方法入参对象添加到模型中
示例

十三丶视图和视图解析器

  • 请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 StringViewModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个 ModelAndView 对象,它包含了逻辑名模型对象的视图
  • Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP,也可能是 ExcelJFreeChart等各种表现形式的视图。
  • 对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工 作上,从而实现 MVC 的充分解耦。

1)视图

我们只需要实现View这个接口就可以自定义视图

示例

@Component
public class HelloView implements View{
    @Override
    public String getContentType() {
        return "text/html";
    }
    @Override
    public void render(Map<String, ?> model, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        response.getWriter().print("hello view, time: " + new Date());
    }
}
@RequestMapping("/testView")
    public String testView(){
        System.out.println("testView");
        return "helloView"; //这里返回的就是我们自定义的视图
    }

2)视图解析器

  • SpringMVC 为逻辑视图名的解析提供了不同的策略,可以在 Spring WEB 上下文中配置一种多种解析策略,并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。
  • 视图解析器的作用比较单一,将逻辑视图解析为一个具体的视图对象
  • 所有的视图解析器都必须实现 ViewResolver 接口。
  • 程序员可以选择一种视图解析器或混用多种视图解析器。
  • 每个视图解析器都实现了Ordered接口并开放出一个 order 属性,可 以通过order 属性指定解析器的优先顺序,order 越小优先级越高
  • SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则将抛出 ServletException 异常

SpringMVC.xml中的配置

<!-- 配置视图解析器: 如何把 handler 方法返回值解析为实际的物理视图 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>


<!-- 配置视图  BeanNameViewResolver 解析器: 使用视图的名字来解析视图 -->
<!-- 通过 order 属性来定义视图解析器的优先级, order 值越小优先级越高 -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
    <property name="order" value="100"></property>
</bean>

来源:公众号–小菜良记 作者:蔡不菜

以上就是W3Cschool编程狮关于(硬核!一文带你深入了解SpringMVC)的相关介绍了,希望对大家有所帮助。

Kotlin跟Java比起来有什么优势?

thbcm阅读(217)

随着计算机语言的发展,从最初的CC++Java等初始语言,近些年来,又开始流行一些被称之为现代编程语言,比如:RustGoKotlinTypeScript等。现代语言流行的原因,小编总结为以下几点:

  1. 入门更容易
  2. 类型推断
  3. 空指针安全
  4. 内置的并发支持
  5. 减少模板代码(简洁)
  6. 操纵集合更容易
  7. 更智能的垃圾回收

以上不一定非常全,但是一定程度上概括了现代语言的特点,下面我们就基于KotlinJava的对比,来看下Kotlin的优势。

new个对象

new一个对象,是我们在编程中最常用的操作之一,让我们先看下如何在Javanew一个对象。

List<String> list =new ArrayList<String>();
list.add("hello world");

Java中,我们需要定义一个变量,然后通过new关键字声明一个ArrayList<String>的示例,这样我们就可以使用他了。

但是在Kotlinnew一个对象会更简洁。

var list:ArrayList<String> = ArrayList<String>()

直接省略了new关键字即可。

类型推断

对于以上的Kotlin代码,我们完全可以省略掉变量 : 后面的类型声明,因为kotlin可以自己推断出来。

val list = ArrayList<String>()

是不是觉得更简洁了?我们开发的效率也更高了。

空指针安全

Java中,变量,方法的参数等都是可以为null的,但是在Kotlin中默认是不允许的,通过这种强制的限制,更好的避免空指针异常。

var list = ArrayList<String>()
list = null

以上代码,在编译期你会得到一个错误提示:

Null can not be a value of a non-null type ArrayList<String>

如果我们的确需要null赋值怎么做呢?在Kotlin中需要开发者自己显示声明才可以。

var list:Array<String>? = null

如上所示,在类型后加 ? 即可。但是注意,我们不提倡这种做法,在实际的开发中,你会发现 ? 大部分都是为了兼容Java代码使用的。

属性

我们通常会把数据和对数据的处理封装到一个类中,如果类中有私有字段,我们还需要提供gettersetter方法提供访问和修改字段的方法。

//Person.java
public  class Person {
  private String name;


  public String getName() {
    return name;
  }


  public void setName(String name) {
    this.name = name;
  }
}
//Main.java
public static void main(String[] args) {
  Person p = new Person();
  p.setName("张三");
  System.out.println(p.getName());
}

以上是我们通过Java实现的一个Person类,并且定义了 name 私有字段,同时提供了gettersetter方法,这样我们才能够使用它。

通过以上代码,大家可以看到,我们为了实现一个 name 的存储,写了很多代码,如果一个类存在很多字段,我们会写更多的不必要的gettersetter方法。

现在我们看在Kotlin中如何实现上面的功能。

//Person.kt
class Person {
  var name:String = ""
}


//main.kt
fun main(){
  val p = Person()
  p.name = "张三"
  println(p.name)
}

是的,就是这么简单,只需要这么几行代码,就可以实现和Java一样的功能,因为Kotlin可以帮我们自动的生成gettersetter这些模板代码,就省了我们很多事情,大大的提高了我们的开发效率,并且整个代码也更简洁。

这里需要注意的是,如果字段是val声明的,那么只会生成getter方法,因为val是不可修改的,等价于Java中的final修饰符;如果字段是var的,可以同时生成gettersetter方法,这时候就可以对字段赋值了。

数据类

Kotlin的简洁不仅仅体现在gettersetter方法上,还有数据类。一个数据类是一个数据容器,它用来存放数据。

一个好的数据类的声明,不仅有私有的字段、gettersetter方法,还要有toStringequalshashCode方法的实现,以便对他们进行打印、比较以及更好的储存在map中。

还是以Person类为例,一个合格的数据类代码如下:

public static  class Person {
  private String name;


  public Person(String name) {
    this.name = name;
  }


  public String getName() {
    return name;
  }


  public void setName(String name) {
    this.name = name;
  }


  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;


    Person person = (Person) o;


    return Objects.equals(name, person.name);
  }


  @Override
  public int hashCode() {
    return name != null ? name.hashCode() : 0;
  }


  @Override
  public String toString() {
    return "Person{" +
      "name='" + name + '\'' +
      '}';
  }
}

看下我们Java的实现,需要有这么30多行代码才能实现。如果我们使用Kotlin会是怎样的呢?

data class Person(val name: String) {}

只需要这么一行代码,以上的Java功能都会实现,这里的关键在于一个 data 修饰符,是不是很酸爽。

并发

Kotlin提供了协程来实现并发,相比JavaThreadExecutor等来说,它更轻便,简洁。我们对比下并发的基本实现。

public static void main(String[] args) throws InterruptedException {
  new MyThread().start();
  System.out.println(Thread.currentThread().getName()+":main");
  //保证JVM存活
  Thread.sleep(1000);
}


private static class MyThread extends Thread{
  @Override
  public void run() {
    try {
      Thread.sleep(500);
      System.out.println(Thread.currentThread().getName()+":Thread");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

运行查看输出,我们发现MyThread并没有阻塞main的执行,也就是并发了。

main:main
Thread-0:Thread

但是要注意到,Java使用了两个线程,一个是main,一个是Thread-0。同样的功能,我们现在使用kotlin实现下:

fun main(){
  runBlocking {
    launch {
      delay(500)
      println("${Thread.currentThread().name}:Thread")
    }
    println("${Thread.currentThread().name}:main")
  }
}

相比Java来说更简洁,而且我们看下打印的输出:

main:main
main:Thread

竟然是在同一个线程上实现的并发,少了一个线程的申请开销,效率更高,这也是kotlin提出协程的概念。如果我们不想让它在main线程上执行,可以通过切换调度器来实现。

launch(Dispatchers.IO)

只需要把上面的代码的launch换成launch(Dispatchers.IO)即可,这样调度器就给我们分配了一个IO的线程池来执行我们的代码。如果我们使用Java来实现,要自己定义线程池,还要提交Runnable,整个代码是非常多的。

main:main
DefaultDispatcher-worker-1:Thread

kotlin的协程非常强大和简洁,通过以上的例子,不能完全展示它的特性,剩下的如协程上下文、调度器、Flow、通道等能力大家可以自己摸索。

小结

通过以上对比,我们应该可以看到作为一门现代编程语言的特点和具备的优势,而且关于Kotlin好用的特性我们还没有完全列举完,比如便捷的集合操作、属性委托、扩展函数等等。

文章来源:www.toutiao.com/a6862537179189477901/

以上就是W3Cschool编程狮关于Kotlin跟Java比起来有什么优势的相关介绍了,希望对大家有所帮助。

总结Python程序员常见的一些问题

thbcm阅读(214)

语用错误

让我们从基础开始,从那些刚学习编程的人钻研语法之前碰到的事情。开始如果你已经编过一些程了,那么以下这些可能看起来十分的简单。如果你曾经尝试过教新手们怎么编程,它们可能就不这么简单了。

在交互提示符中输入的Python代码

在>>>交互提示符中你只能输入的Python代码,而不是系统命令。时常有人在这个提示符下输入的emacsLS,或者编辑之类的命令,这些可不是Python的代码。在Python的代码中确实有办法来调用系统命令(例如os.system和os.popen),但可不是像直接输入命令这么直接。如果你想要在交互提示符中启动一个Python文件,请用导入文件,而不是系统命令python file.py.

打印语句(仅仅)是在文件中需要

因为交互解释器会自动的将表达式的结果输出,所以你不需要交互的键入完整的打印语句。这是个很棒的功能,但是记住在代码文件里,通常你只有用打印语句才能看得到输出。

小心的Windows里的自动扩展名

如果你在Windows里使用记事本来编辑代码文件的话,当你保持的时候小心选择“所有文件”(所有文件)这个类型,并且明确的给你的文件加一个.py的后缀。不然的话记事本会给你的文件加一个.TXT的扩展名,使得在某些启动方法中没法跑这个程序。更糟糕的是,像字或者是写字板一类的文字处理软件还会默认的加上一些格式字符,而这些字符Python语法是不认的。所以记得,在Windows下总是选“所有文件”(所有文件),并保存为纯文本,或者使用更加“编程友好”的文本编辑工具,比如IDLE 。在IDLE中,记得在保存时手动加上的.py的扩展名。

在视窗下点击图标的问题

在视窗下,你能靠点击Python的文件来启动一个Python的程序,但这有时会有问题。首先,程序的输出窗口在程序结束的瞬间也就消失了,要让它不消失,你可以在文件最后加一条的raw_input()的调用另外,记住如果有错的话,输出窗口也就立即消失了要看到你的错误信息的话,用别的方法来调用你的程序:。比如从系统命令行启动,通过提示符下用进口语句,或者IDLE菜单里的选项,等等。

导入只在第一次有效

你可以在交互提示符中通过输入一个文件来运行它,但是这只会在一个会话中起一次作用。接下来的进口仅仅是返回这个已经加载的模块要想强制Python的重新加载一个文件的代码,请调用函数重载(模块)来达到这个目的。注意对重新加载请使用括号,而进口不要使用括号。

空白行(仅仅)在交互提示符中有作用

在模块文件中空白行和注释统统会被忽略掉,但是在交互提示符中键入代码时,空白行表示一个复合语句的结束换句话说,空白行告诉交互提示符你完成了一个复合语句。在你真正完成之前不要键入回车事实上当你要开始一个新的语句时,你需要键入一个空行来结束当前的语句 – 交互提示符一次只运行一条语句。

代码错误

一旦你开始认真写的Python代码了,接下来一堆陷阱就更加危险了 , 这些都是一些跨语言特性的基本代码错误,并常常困扰不细心的程序员。

别忘了冒号

这是新手程序员最容易犯的一个错误:别忘了在复合语句的起始语句(if,while,for wait语句的第一行)结束的地方加上一个冒号“”。也许你刚开始会忘掉这个,但是到了很快这就会成为一个下意识的习惯。课堂里 75% 的学生当天就可以记住这个。

初始化变量

在的Python里,一个表达式中的名字在它被赋值之前是没法使用的这是有意而为的:这样能避免一些输入失误,同时也能避免默认究竟应该是什么类型的问题(0,无“,”,[] ,?)。记住把计数器初始化为 0 ,列表初始化为[],以此类推。

从第一列开始

确保把顶层的,未嵌套的代码放在最左边第一列开始。这包括在模块文件中未嵌套的代码,以及在交互提示符中未嵌套的代码.Python使用缩进的办法来区分嵌套的代码段,因此在你代码左边的空格意味着嵌套的代码块。除了缩进以外,空格通常是被忽略掉的。

缩进一致

在同一个代码块中避免讲选项卡和空格混用来缩进,除非你知道运行你的代码的系统是怎么处理标签的。否则的话,在你的编辑器里看起来是标签的缩进也许Python的看起来就会被视作是一些空格保险起见,在每个代码块中全都是用标签或者全都是用空格来缩进;用多少由你决定。

在函数调用时使用括号

无论一个函数是否需要参数,你必须要加一对括号来调用它。即,使用函数(),而不是function.Python的函数简单来说是具有特殊功能(调用)的对象,而调用是用括号来触发的。像所有的对象一样,他们也可以被赋值给变量,并且间接的使用他们:x = function:x()。在Python的培训中,这样的错误常常在文件的操作中出现。会看到新手用file.close来关闭一个问题,而不是用file.close()。因为在Python的中引用一个函数而不调用它是合法的,因此不使用括号的操作(file.close)无声的成功了,但是并没有关闭这个文件!

在导入时不要使用表达式或者路径

在系统的命令行里使用文件夹路径或者文件的扩展名,但不要在import语句中使用。即,使用import mod,而不是import mod.py,或者import dir / mod.py。在实际情况中,这大概是初学者常犯的第二大错误了。因为模块会有除了的.py以外的其他的后缀(例如,pyc文件),强制写上某个后缀不仅是不合语法的,也没有什么意义。和系统有关的目录路径的格式是从你的模块搜索路径的设置里来的,而不是import语句。你可以在文件名里使用点来指向包的子目录(例如,import dir1.dir2.mod) ,但是最左边的目录必须得通过模块搜索路径能够找到,并且没有在导入中没有其他路径格式。不正确的语句import mod.pyPython认为是要记载一个包,它先加载一个模块mod,然后试图通过在一个叫做MOD的目录里去找到叫做吡啶的模块,最后可能什么也找不到而报出一系列费解的错误信息。

不要在Python的中写Ç代码

以下是给不熟悉的Pythonç程序员的一些备忘贴士:

在和条件测试时,不用输入括号(例如,如果(X == 1):)。如果你喜欢的话,加上括号也无妨,只是在这里是完全多余的。

不要用分号来结束你的语句从技术上讲这在Python中里是合法的,但是这毫无用处,除非你要把很多语句放在同一行里(例如,X = 1。Y = 2; Z = 3)。

不要在while循环的条件测试中嵌入赋值语句(例如,while((x = next()!= NULL))。在Python中,需要表达式的地方不能出现语句,并且赋值语句不是一个表达式。

编程错误

下面终于要讲到当你用到更多的Python中的功能(数据类型,函数,模块,类等等)时可能碰到的问题了。由于篇幅有限,这里尽量精简,尤其是对一些高级的概念。要想了解更多的细节,敬请阅读学习Python,第2版的“小贴士”以及“Gotchas”章节。

打开文件的调用不使用模块搜索路径

当你在Python的中调用的open()来访问一个外部的文件时,Python中不会使用模块搜索路径来定位这个目标文件。它会使用你提供的绝对路径,或者假定这个文件是在当前工作目录中。模块搜索路径仅仅为模块加载服务的。

不同的类型对应的方法也不同

列表的方法是不能用在字符串上的,反之亦然。通常情况下,方法的调用是和数据类型有关的,但是内部函数通常在很多类型上都可以使用。举个例子来说,列表的反转方法仅仅对列表有用,但是LEN函数对任何具有长度的对象都适用

不能直接改变不可变数据类型

记住你没法直接的改变一个不可变的对象(例如,元组,字符串):

T = (1,  2,  3)
T[2]  =  4          # 错误

用切片,联接等构建一个新的对象,并根据需求将原来变量的值赋给它因为Python中会自动回收没有用的内存,因此这没有看起来那么浪费:

T = T[:2] + (4,)  # 没问题了: T 变成了 (1, 2, 4)

使用简单的用于循环而不是同时或者范围

当你要从左到右遍历一个有序的对象的所有元素时,用简单的for循环(例如,for x in seq :)相比于基于while-或者range-的计数循环而言会更容易写,通常运行起来也更快除非你一定需要,尽量避免在一个用于循环里使用范围:。让的Python来替你解决标号的问题在下面的例子中三个循环结构都没有问题,但是第一个通常来说更好;在Python的里,简单至上。

S = "lumberjack"for c in S: print c                   # 最简单for i in range(len(S)): print S[i]    # 太多了i = 0                                 # 太多了while i < len(S): print S[i]; i += 1

不要试图从那些会改变对象的函数得到结果

诸如像方法list.append()list.sort()一类的直接改变操作会改变一个对象,但不会将它们改变的对象返回出来(它们会返回无);正确的做法是直接调用它们而不要将结果赋值经常会看见初学者会写诸如此类的代码:

mylist = mylist.append(X)

目的是要得到附加的结果,但是事实上这样做会将无赋值给MYLIST,而不是改变后的列表。更加特别的一个例子是想通过用排序后的键值来遍历一个字典里的各个元素,请看下面的例子:

D = {...}
for k in D.keys().sort(): print D[k]

差一点儿就成功了--keys方法会创建一个键的列表,然后用某种方法来将这个列表排序 – 但是因为某种方法会返回无,这个循环会失败,因为它实际上是要遍历无(这可不是一个序列)要改正这段代码,将方法的调用分离出来,放在不同的语句中,如下:

Ks = D.keys()
Ks.sort()
for k in Ks: print D[k]

只有在数字类型中才存在类型转换

在的Python中,一个诸如123 + 3.145的表达式是可以工作的 – 它会自动将整数型转换为浮点型,然后用浮点运算但是下面的代码就会出错了。:

S = "42"
I = 1
X = S + I        # 类型错误

这同样也是有意而为的,因为这是不明确的:究竟是将字符串转换为数字(进行相加)呢,还是将数字转换为字符串(进行连接)呢在Python的中,我们认为“明确比含糊好“(即,EIBTI(明确比隐含更好)),因此你得手动转换类型:

X = int(S) + I   # 做加法: 43
X = S + str(I)   # 字符串联接: "421"

循环的数据结构会导致循环

尽管这在实际情况中很少见,但是如果一个对象的集合包含了到它自己的引用,这被称为循环对象(cyclic object)。如果在一个对象中发现一个循环,Python会输出一个[... ],以避免在无限循环中卡住:

>>> L = ['grail']  # 在 L中又引用L自身会
>>> L.append(L)    # 在对象中创造一个循环
>>> L['grail', [...]]

除了知道这三个点在对象中表示循环以外,这个例子也是很值得借鉴的。因为你可能无意间在你的代码中出现这样的循环的结构而导致你的代码出错。如果有必要的话,维护一个列表或者字典来表示已经访问过的对象,然后通过检查它来确认你是否碰到了循环。

赋值语句不会创建对象的副本,仅仅创建引用

这是的Python的一个核心理念,有时候当行为不对时会带来错误。在下面的例子中,一个列表对象被赋给了名为大号的变量,然后大号又在列表中号中被引用。内部改变大号的话,同时也会改变中号所引用的对象,因为它们俩都指向同一个对象。

>>> L = [1, 2, 3]        # 共用的列表对象
>>> M = ['X', L, 'Y']    # 嵌入一个到L的引用
>>> M
['X', [1, 2, 3], 'Y']
>>> L[1] = 0             # 也改变了M
>>> M
['X', [1, 0, 3], 'Y']

通常情况下只有在稍大一点的程序里这就显得很重要了,而且这些共用的引用通常确实是你需要的如果不是的话,你可以明确的给他们创建一个副本来避免共用的引用;对于列表来说,你可以通过使用一个空列表的切片来创建一个顶层的副本:

>>> L = [1, 2, 3]
>>> M = ['X', L[:], 'Y']   # 嵌入一个L的副本
>>> L[1] = 0               # 仅仅改变了L,但是不影响M
>>> L
[1, 0, 3]
>>> M
['X', [1, 2, 3], 'Y']

切片的范围起始从默认的 0 到被切片的序列的最大长度。如果两者都省略掉了,那么切片会抽取该序列中的所有元素,并创造一个顶层的副本(一个新的,不被公用的对象)。对于字典来说,使用字典的dict.copy()方法。

静态识别本地域的变量名

Python的默认将一个函数中赋值的变量名视作是本地域的,它们存在于该函数的作用域中并且仅仅在函数运行的时候才存在。从技术上讲,巨蟒是在编译DEF代码时,去静态的识别本地变量,而不是在运行时碰到赋值的时候才识别到的。如果不理解这点的话,会引起人们的误解。比如,看看下面的例子,当你在一个引用之后给一个变量赋值会怎么样:

>>> X = 99
>>> def func():
...     print X      # 这个时候还不存在
          ... 
        >>> func( )          # 出错了!

你会得到一个“未定义变量名”的错误,但是其原因是很微妙的。当编译这则代码时,Python中碰到给 X 赋值的语句时认为在这个函数中的任何地方X会被视作。一个本地变量名但是之后当真正运行这个函数时,执行打印语句的时候,赋值语句还没有发生,这样的Python便会报告一个“未定义变量名”的错误。

事实上,之前的这个例子想要做的事情是很模糊的:你是想要先输出那个全局的 X ,然后创建一个本地的 X 呢,还是说这是个程序的错误如果你真的是想要输出这个全局的 X ,你需要将它在一个全局语句中声明它,或者通过包络模块的名字来引用它。

默认参数和可变对象

在执行DEF语句时,默认参数的值只被解析并保存一次,而不是每次在调用函数的时候。这通常是你想要的那样,但是因为默认值需要在每次调用时都保持同样对象,你在试图改变可变的默认值(mutable defaults)的时候可要小心了。例如,下面的函数中使用一个空的列表作为默认值,然后在之后每一次函数调用的时候改变它的值:

>>> def saver(x=[]):   # 保存一个列表对象
...     x.append(1)    # 并每次调用的时候
...     print x        # 改变它的值
...
>>> saver([2])         # 未使用默认值
[2, 1]
>>> saver()            # 使用默认值
[1]
>>> saver()            # 每次调用都会增加!
[1, 1]
>>> saver()
[1, 1, 1]

有的人将这个视作Python中的一个特点 – 因为可变的默认参数在每次函数调用时保持了它们的状态,它们能提供像 ç 语言中静态本地函数变量的类似的一些功能但是,当你第一次碰到它时会觉得这很奇怪,并且在的Python中有更加简单的办法来在不同的调用之间保存状态(比如说类)。

要摆脱这样的行为,在函数开始的地方用切片或者方法来创建默认参数的副本,或者将默认值的表达式移到函数里面;只要每次函数调用时这些值在函数里,就会每次都得到一个新的对象:

>>> def saver(x=None):
  ...     if x is None: x = []   # 没有传入参数?
    ...     x.append(1)            # 改变新的列表
      ...     print x
        ...
          >>> saver([2])                 # 没有使用默认值
        [2, 1]
>>> saver()                    # 这次不会变了
        [1
        ]>>> saver()[1]

其他常见的编程陷阱

下面列举了其他的一些在这里没法详述的陷阱:

在顶层文件中语句的顺序是有讲究的:因为运行或者加载一个文件会从上到下运行它的语句,所以请确保将你未嵌套的函数调用或者类的调用放在函数或者类的定义之后。

重装不影响用从加载的名字:重装最好和导入语句一起使用如果你使用的语句,记得在重装之后重新运行一遍从,否则你仍然使用之前老的名字。

在多重继承中混合的顺序是有讲究的:这是因为对超类的搜索是从左到右的,在类定义的头部,在多重超类中如果出现重复的名字,则以最左边的类名为准。

在尝试语句中空的,除了子句可能会比你预想的捕捉到更多的错误。在尝试语句中空的,除了子句表示捕捉所有的错误,即便是真正的程序错误,和sys.exit()调用,也会被捕捉到。

以上就是W3Cschool编程狮关于总结Python程序员常见的一些问题的相关介绍了,希望对大家有所帮助。

如何对Linux上的文件进行合并和排序

thbcm阅读(219)

Linux上对文件进行合并和排序的方法有很多,但使用哪种就取决于你想怎么做,比如:你是只想将多个文件的内容放入一个文件中,还是以某种方式组织它,让它更易于使用。接下来,小编将带你看看一些用于排序和合并文件内容的命令,让你了解它们的特点。

使用 cat

如果你只想将一组文件放到单个文件中,那么 cat 命令是一个容易的选择。你所要做的就是输入 cat,然后按你希望它们在合并文件中的顺序在命令行中列出这些文件。将命令的输出重定向到要创建的文件。如果指定名称的文件已经存在,那么文件将被覆盖。例如:

$ cat firstfile secondfile thirdfile > newfile

如果要将一系列文件的内容添加到现有文件中,而不是覆盖它,只需将 > 变成 >>

$ cat firstfile secondfile thirdfile >> updated_file

如果你要合并的文件遵循一些方便的命名约定,那么任务可能更简单。如果可以使用正则表达式指定所有文件名,那就不必列出所有文件。例如,如果文件全部以 file 结束,如上所示,你可以进行如下操作:

$ cat *file > allfiles

请注意,上面的命令将按字母数字顺序添加文件内容。在 Linux 上,一个名为 filea 的文件将排在名为 fileA 的文件的前面,但会在 file7 的后面。毕竟,当我们处理字母数字序列时,我们不仅需要考虑 ABCDE,还需要考虑 0123456789aAbBcCdDeE。你可以使用 ls *file 这样的命令来查看合并文件之前文件的顺序。

注意:首先确保你的命令包含合并文件中所需的所有文件,而不是其他文件,尤其是你使用 * 等通配符时。不要忘记,用于合并的文件仍将单独存在,在确认合并后,你可能想要删除这些文件。

(推荐教程:Linux教程

按时间期限合并文件

如果要基于每个文件的时间期限而不是文件名来合并文件,请使用以下命令:

$ for file in `ls -tr myfile.*`; do  cat $file >> BigFile.$$; done

使用 -tr 选项(t = 时间,r = 反向)将产生按照最早的在最前排列的文件列表。例如,如果你要保留某些活动的日志,并且希望按活动执行的顺序添加内容,则这非常有用。

上面命令中的 $ $ 表示运行命令时的进程 ID。不是很必要使用此功能,但它几乎不可能会无意添加到现有的文件,而不是创建新文件。如果使用 $$,那么生成的文件可能如下所示:

$ ls -l BigFile.*
-rw-rw-r-- 1 justme justme   931725 Aug  6 12:36 BigFile.582914

合并和排序文件

Linux 提供了一些有趣的方式来对合并之前或之后的文件内容进行排序。

按字母对内容进行排序

如果要对合并的文件内容进行排序,那么可以使用以下命令对整体内容进行排序:

$ cat myfile.1 myfile.2 myfile.3 | sort > newfile

如果要按文件对内容进行分组,请使用以下命令对每个文件进行排序,然后再将它添加到新文件中:

$ for file in `ls myfile.?`; do sort $file >> newfile; done

对文件进行数字排序

要对文件内容进行数字排序,请在 sort 中使用 -n 选项。仅当文件中的行以数字开头时,此选项才有用。请记住,按照默认顺序,02 将小于 1。当你要确保行以数字排序时,请使用 -n 选项。

$ cat myfile.1 myfile.2 myfile.3 | sort -n > xyz

如果文件中的行以 2020-11-03 或 2020/11/03(年月日格式)这样的日期格式开头,-n 选项还能让你按日期对内容进行排序。其他格式的日期排序将非常棘手,并且将需要更复杂的命令。

使用 paste

paste 命令允许你逐行连接文件内容。使用此命令时,合并文件的第一行将包含要合并的每个文件的第一行。以下是示例,其中我使用了大写字母以便于查看行的来源:

$ cat file.a
A one
A two
A three


$ paste file.a file.b file.c
A one   B one   C one
A two   B two   C two
A three B three C thee
        B four  C four
                C five

将输出重定向到另一个文件来保存它:

$ paste file.a file.b file.c > merged_content

或者,你可以将每个文件的内容在同一行中合并,然后将文件粘贴在一起。这需要使用 -s(序列)选项。注意这次的输出如何显示每个文件的内容:

$ paste -s file.a file.b file.c
A one   A two   A three
B one   B two   B three B four
C one   C two   C thee  C four  C five

使用 join

合并文件的另一个命令是 joinjoin 命令让你能基于一个共同字段合并多个文件的内容。例如,你可能有一个包含一组同事的电话的文件,其中,而另一个包含了同事的电子邮件地址,并且两者均按个人姓名列出。你可以使用 join 创建一个包含电话和电子邮件地址的文件。

一个重要的限制是文件的行必须是相同的顺序,并在每个文件中包括用于连接的字段。

这是一个示例命令:

$ join phone_numbers email_addresses
Sandra 555-456-1234 bugfarm@gmail.com
Pedro 555-540-5405
John 555-333-1234 john_doe@gmail.com
Nemo 555-123-4567 cutie@fish.com

在本例中,即使缺少附加信息,第一个字段(名字)也必须存在于每个文件中,否则命令会因错误而失败。对内容进行排序有帮助,而且可能更容易管理,但只要顺序一致,就不需要这么做。

(推荐微课:Linux微课

总结

Linux 上,你有很多可以合并和排序存储在单独文件中的数据的方式。这些方法可以使原本繁琐的任务变得异常简单。

文章来源:www.toutiao.com/a6863629427096420875/

以上就是W3Cschool编程狮关于 如何对Linux上的文件进行合并和排序 的相关介绍了,希望对大家有所帮助。

mysql 主从复制配置说明

thbcm阅读(208)

MySQL数据库支持单向、双向、链式级联、环状等不同业务场景的复制。在复制过程中,一台服务器充当主服务器(Master),接收来自用户的内容更新,而一个或多个其他的服务器充当从服务器(Slave),接收来自主服务器binlog文件的日志内容,解析出SQL,重新更新到从数据库,使得主从服务器的数据达到一致。

mysql 主从复制 配置

mysql 的 默认配置文件在 /etc/my.cnf

1 修改主库 配置文件:

设置 服务id,并且开启二进制日志文件。

server-id=1 log-bin=mysql-bin

2重启服务:service mysqld restart;

3 连接mysql创建用户,和授权:

 CREATE USER ‘zyk’@’132.232.37.228’ IDENTIFIED BY ‘zyk123’


  GRANT REPLICATION SLAVE ON *.* TO 'zyk'@'132.232.37.228';


  flush privileges;

4 查看主机 master状态;

SHOW MASTER STATUS;

5 修改从库配置: 修改 服务Id ,保持唯一

6 在 mysql命令行执行 下面连接master 语句。 注意参数来源于上面的 过程。

CHANGE MASTER TO MASTER_HOST=’cxygg.top’, MASTER_USER=’zyk’, MASTER_PASSWORD=’zyk123′, MASTER_LOG_FILE=’mysql-bin.000001′, MASTER_LOG_POS=771;

7 启动从机模式

start slave;

8 看看从库状态。 箭头 部分 Ok ,一般就没问题。

show slave status;  

以上就是W3Cschool编程狮关于 mysql 主从复制配置 的相关介绍了,希望对大家有所帮助。

MATLAB基于14种聚类方法的分析

thbcm阅读(190)

聚类分析又称群分析,它是用来研究样品分类问题的统计分析方法,同时也是一种重要的数据挖掘算法。聚类分析是由若干模式组成的,通常,模式是一个度量的向量,聚类分析以相似性为基础,在一个聚类中的模式之间比不在同一聚类中的模式之间具有更多的相似性。

对于聚类算法,大多数用SPSS软件实现,通常导入数据,并且选择聚类方法即可实现,本文借用MATLAB软件,基于14种不同的聚类分析方法,实现样品聚类。

14种聚类方法

(1)最长距离法

X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
D=pdist(X,'euclid');
M=squareform(D);
Z=linkage(D,'complete');
H=dendrogram(Z);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D);
T=cluster(Z,3);

(2) 最短距离法

X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
D=pdist(X,'euclid');
M=squareform(D);
Z=linkage(D,'single')
;H=dendrogram(Z);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D);
T=cluster(Z,'cutoff',0.8);

(3)综合聚类子程序

X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
T=clusterdata(X,0.8);
Re=find(T=5)

(4)重心法&标准欧氏距离

S=['福冈';'合肥';'武汉';'长沙';'桂林';'温州';'成都'];
X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
D=pdist(X,'seuclid');
M=squareform(D);
Z=linkage(D,'centroid');
H=dendrogram(Z,'labels',S);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D);
T=cluster(Z,3);

(5)重心法&欧氏距离平方

S=['福冈';'合肥';'武汉';'长沙';'桂林';'温州';'成都'];
X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
D=pdist(X,'euclid');
D2=D.^2;
M=squareform(D2);
Z=linkage(D2,'centroid');
H=dendrogram(Z,'labels',S);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D2);
T=cluster(Z,3);

(6)重心法&精度加权距离

S=['福冈';'合肥';'武汉';'长沙';'桂林';'温州';'成都'];
X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
[n,m]=size(X);
stdx=std(X);
X2=X./stdx(ones(n,1),:);
D=pdist(X2,'euclid');
M=squareform(D);
Z=linkage(D,'centroid');
H=dendrogram(Z,'labels',S);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D);
T=cluster(Z,3);

(7)最短距离法&基于主成分的标准欧式距离

S=['福冈';'合肥';'武汉';'长沙';'桂林';'温州';'成都'];
X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
[E,score,eigen,T]=princomp(X);
D=pdist(score,'seuclid');
M=squareform(D);
Z=linkage(D,'single');
H=dendrogram(Z,'labels',S);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D);
T=cluster(Z,3);

(8)平均法&标准欧式距离

S=['福冈';'合肥';'武汉';'长沙';'桂林';'温州';'成都'];
X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
D=pdist(X,'seuclid');
M=squareform(D);
Z=linkage(D,'average');
H=dendrogram(Z,'labels',S);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D);
T=cluster(Z,3);

(9)权重法&标准欧式距离

S=['福冈';'合肥';'武汉';'长沙';'桂林';'温州';'成都'];
X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
D=pdist(X,'seuclid');
M=squareform(D);
Z=linkage(D,'weighted');
H=dendrogram(Z,'labels',S);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D);
T=cluster(Z,3);

(10)最短距离法&马氏距离

S=['福冈';'合肥';'武汉';'长沙';'桂林';'温州';'成都'];
X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
D=pdist(X,'mahal');M=squareform(D);Z=linkage(D,'single');H=dendrogram(Z,'labels',S);xlabel('City');ylabel('Scale');C=cophenet(Z,D);T=cluster(Z,3);

(11)重心法&标准化数据的的欧式距离

S=['福冈';'合肥';'武汉';'长沙';'桂林';'温州';'成都'];
X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
[n,m]=size(X);
mv=mean(X);
st=std(X);
x=(X-mv(ones(n,1),:))./st(ones(n,1),:);
D=pdist(X,'euclid');
M=squareform(D);
Z=linkage(D,'centroid');
H=dendrogram(Z,'labels',S);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D);
T=cluster(Z,3);

(12)最长距离法&欧式距离

S=['福冈';'合肥';'武汉';'长沙';'桂林';'温州';'成都'];
X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
D=pdist(X,'euclid');
M=squareform(D);
Z=linkage(D,'complete');
[H tPerm]=dendrogram(Z,'labels',S);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D);
T=cluster(Z,3);

(13)平均法&相似系数

S=['福冈';'合肥';'武汉';'长沙';'桂林';'温州';'成都'];
X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
D=pdist(X,'cosine');
M=squareform(D);
Z=linkage(D,'centroid');
T=dendrogram(Z,'labels',S);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D);
T=cluster(Z,3);

(14)最短距离法&基于主成分的标准欧式距离

S=['福冈';'合肥';'武汉';'长沙';'桂林';'温州';'成都'];
X=[16.21492 2000 -8.2 6.2;
   15.7 970 2209 -20.6 1.9;
   16.3 1260 2085 -17.3 2.8;
   17.2 14221726 -9.5 4.6;
   18.8 1874 1709 -4.9 8.0;
   17.9 1698 1848 -4.5 7.5;
   16.3 976 1239-4.6 5.6];
[E,score,eigen,T]=princomp(X);
PCA=[score(:,1),score(:,2)];
D=pdist(PCA,'seuclid');
M=squareform(D);
Z=linkage(D,'single');
H=dendrogram(Z,'labels',S);
xlabel('City');
ylabel('Scale');
C=cophenet(Z,D);
T=cluster(Z,3);

(推荐教程:MATLAB 教程

文章来源:www.toutiao.com/a6863649930347545091/

以上就是W3Cschool编程狮关于 MATLAB基于14种聚类方法的分析 的相关介绍了,希望对大家有所帮助。

Oracle数据库如何限制IP访问

thbcm阅读(196)

一、概述

本文将会给大家介绍在Oracle数据库该如何限制某个IP访问,或者限定某个IP段才能访问。

  1. 通过sqlnet.ora
  2. 通过/etc/hosts.deny/etc/hosts.allow
  3. 通过iptables

二、正式实验

本次实验环境是Centos6.10 + Oracle 11.2.0.4单实例,数据库服务器ip地址为192.168.31.71

1. 通过sqlnet.ora

a. 关闭数据库服务器上的防火墙,修改sqlnet.ora文件

该文件放在$ORACLE_HOME/network/admin下,如果没有就在该目录下创建一个即可 添加以下两行

tcp.validnode_checking = yes
tcp.invited_nodes = (192.168.31.71, 192.168.31.77)

这里需要注意的是必须把本机ip地址加进来(不能写成localhost和127.0.0.1),否则监听启动会报错

b. 重启监听,让sqlnet.ora的修改生效

lsnrctl stop
lsnrctl start

设置之后就只有这两个ip地址192.168.31.71, 192.168.31.77能访问数据库,其它ip地址访问会报ORA-12547: TNS:lost contact错误 tcp.invited_nodes 的意思是开通白名单,不在白名单中的一律拒绝访问,它也可以写成(192.168.31.*, 192.168.31.0/24)等方式,表明这个网段都能访问 另外还有个参数tcp.excluded_nodes,表示黑名单,这里不做介绍,有兴趣的可以自己去做做实验

(推荐教程:Oracle教程

2. 通过/etc/hosts.deny和/etc/hosts.allow

sqlnet.ora属于数据库层面的限制,但如果一个ip能够使用root或者oraclessh到这台数据库服务器的话,那么它依然能够访问数据库。为了避免这种情况,这时就需要通过/etc/hosts.allow/etc/hosts.deny去限制某个ip或者ip段才能ssh访问数据库服务器

先删除前面实验添加的sqlnet.ora,然后重启监听

lsnrctl stop
lsnrctl start

a. 修改/etc/hosts.deny

在文件尾部添加一行

all:all:deny

第一个all表示禁掉所有使用tcp_wrappers库的服务,举例来说就是sshtelnet等服务 第二个all表示所有网段

b. 修改/etc/hosts.allow

在前面一步中我禁掉所有的网段,所以在这一步中要开通指定的网段

修改/etc/hosts.allow,在文件尾部添加

all:192.168.31.71:allow
all:192.168.31.47:allow

格式与hosts.deny一样,第一行表示把本机放开,第二行表示给.47开通白名单

下面用我另外一台机器(即不在allow中的)sshtelnet连接 71 这个机器,就会出现如下报错

[oracle@oracle19c1 ~]$ ssh 192.168.31.71
ssh_exchange_identification: read: Connection reset by peer


[oracle@oracle19c1 ~]$ telnet 192.168.31.71 22
Trying 192.168.31.71...
Connected to 192.168.31.71.
Escape character is '^]'.
Connection closed by foreign host.

连数据库却不受影响,因为数据库服务不归hosts.denyhosts.allow

[oracle@oracle19c1 ~]$ sqlplus sys/xxxxx@192.168.31.71:1521/orcltest as sysdba


SQL*Plus: Release 19.0.0.0.0 - Production on Sun Aug 16 23:12:49 2020
Version 19.3.0.0.0


Copyright (c) 1982, 2019, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

其中 ip 地址也可以换成以下的写法 通配符的形式 192.168.31.*表示192.168.31这个网段 网段/掩码 192.168.31.0/255.255.255.0也表示192.168.31这个网段

3. 通过iptables

sqlnet.ora能够限制数据库的访问,/etc/hosts.deny/etc/hosts.allow能够限制ssh的访问,那有没有办法既能限制数据库的访问,也能限制ssh的访问呢,答案就是linux自带的防火墙功能了。 为了实验,将前面做的修改全部清除。

使用root执行以下命令

service iptables start  # 打开防火墙服务
iptables -I INPUT -s 192.168.31.0/24 -p tcp --dport 1521 -j ACCEPT  # 允许192.168.31网段的ip访问本机1521端口
iptables -I INPUT ! -s 192.168.31.0/24 -p tcp --dport 22 -j DROP  # 拒绝非192.168.31网段的ip访问本机22端口
service iptables save  # 规则保存到配置文件/etc/sysconfig/iptables中

这样就同时限制了其它ip对服务器的ssh和数据库访问

一些扩展知识: iptables -L -n --line-numbers #查看当前系统中的iptables iptables -D INPUT 2 #删除input链中编号为 2 的规则,编号数字可以通过上一个命令得到

(推荐微课:Oracle数据库入门到实战

三、总结

  1. 如果只是限制其它ip对数据库的访问,使用sqlnet.ora
  2. 如果要限制其它ip对数据库所在服务器上的ssh连接,使用/etc/hosts.deny/etc/hosts.allow
  3. 前面两个配合起来,基本上就能保证你的数据库安全了。但是如果你对linuxiptables很熟悉,那么直接使用iptables去限制。
  4. 使用/etc/hosts.denyiptables时一定要保证自己的操作机能连到服务器,不然很容易就把自己锁死在外面了。

文章来自(墨天轮),来源:www.modb.pro/db/29270?ywm= 作者:杨豹

以上就是W3Cschool编程狮关于 Oracle数据库如何限制IP访问 的相关介绍了,希望对大家有所帮助。

联系我们