Insightcodeyk


  • 首页

  • 分类

  • 标签

  • 归档

HashMap机制与要点

发表于 2018-04-19 | 分类于 JDKSource

作者: insightcodeyk
出处: https://insightcodeyk.github.io/2018/04/19/HashMap/
声明: 本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。

1 HashMap存储结构

1.1 重要成员变量

1.transient Node<K,V>[] table;//Hash桶数组
这是以key为index的数组,也就是说这个数组的就是HashMap键值对的物理结构

2.int threshold;//table数组所能容纳的上限值,超过就会调用resize().  (threshold = capacity * load factor).

3.finale float loadFactor;//装载因子
设置的table数组的容量比例,超过容量比例就会扩容,扩大为原来的两倍。设置装载因子的目的是提高HashMap查找效率

4.static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认装载因子
若初始化没有指定装载因子,则默认使用0.75为容量比例。

5.static final int TREEIFY_THRESHOLD = 8;//链表上限
在重复的Key链表节点超过TREEIFY_THRESHOLD之后对Key链表节点结构进行重构为红黑树

6.static final int UNTREEIFY_THRESHOLD = 6;//红黑树下限
当红黑树节点小于等于6时,红黑树重构为单链表

7.staitc final int MIN_TREEIFY_CAPACITY = 64;//扩容和红黑树化的阈值
当table数组容量大于等于64时,哈希桶节点增加超过8则进行红黑树化,否则进行扩容

1.2 HashMap存储组织结构

HashMap中的存储结构,Hash桶数组的结构如下图:

通过这个结构图可以通过几个简单的要点罗列对HashMap的组织结构有一个直观的认知:
1.HashMap维护一个数组table
2.table的index是由HashMap定义的hash算法根据传入的key对象计算出
3.HashMap使用链地址法
4.当链长度小于等于8时,相同Index的键值对以单链表形式存储;当链大于8时,若table长度大于等于64,则相同Index的键值对组织形式重构为红黑树;否则进行resize()

2 HashMap实现要点

理解HashMap的实现方式有三个要点:

2.1 Key值和Hash桶数组index的映射关系

2.2 扩容机制

2.3 HashMap中put方法的实现方式

3 JDK1.7,JDK1.8的区别

4 Tips and HighLight

Intellij IDEA配置Tomcat运行环境

发表于 2018-04-19 | 分类于 Tomcat

作者: insightcodeyk
出处: https://insightcodeyk.github.io/2018/04/19/IntellijIDEAAndTomcatCircumstance/
声明: 本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。

阅读本篇文章之前需要对Tomcat目录结构有基本了解,以下版本是笔者使用版本,其他各个版本的Intellij IDEA操作类似

1 版本

Intellij IDEA: 2017.3.1
JDK: 1.8.0_152
Tomcat: 8.5.24
Windows: win8

2 配置步骤

2.1 建立新工程


以上图片里面有5个标号
1.选择JavaEE工程
2.选择Web应用程序
3.点击New选择你的JDK包位置
4.点击New选择你的Tomcat包位置
5.点击下一步

2.2 选择工程位置


以上图片里有3个标号
1.选择自己的工程名称
2.选择自己的工程位置
3.点击完成

2.3 配置Idea中Tomcat结构

2.3.1 定位Tomcat目录结构模块


以上图片里有1个标号
1.打开当前工程结构,出现下图所示菜单框


以上图片里有4个标号
依次点击1,2,3标识的选项出现标号4所示目录结构

2.3.2 创建Tomcat目录结构


以上图片里有3个标号
通过右键选择标号1选项依次创建如标号2所示的Tomcat目录结构,最后点击标号3选项OK

2.3.3 设置Idea字节码文件输出位置


以上图片里有3个标号
依次选择1,2标号所示选项,对标号3所示的两个路径更改为上一步中创建的classes文件路径,然后点击标号4应用保存

2.3.4 设置Tomcat的lib包依赖


以上图片里有4个标号
依次选择标号1,2所示选项,弹框中选择标号3所示选项,出现下图


以上图片里面有2个标号
依次点击1,2标号选项,会出现如下图所示内容


以上图片里面有2个标号
1.选择Tomcat目录结构中的lib目录
2.确定选项

2.4 配置Tomcat运行环境


以上图片里有2个标号
依次点击两个标号出现运行环境配置如下图


以上图片里有3个标号
1.点击标号1出现菜单
2.若能找到如标号2所示就不用点标号3
3.若找不到标号2所示Tomcat server则点击标号3扩展栏查找,点击选择你的loacl或者romate,Tomcat主机后出现下面的界面


以上图片有4个标号
1.输入你的运行环境名字
2.点击标号2所示选项
3.点击标号3所示出现选择菜单
4.点击标号4所示选项出现以下菜单


以上图片中有2个标号
1.输入你的Web应用程序入口
2.确定应用,现在你已经完成了你的整个配置

3 Tips

3.1 热部署(debug模式下)

设置热部署方式后,不需要每次更改文件都要重新部署Tomcat


以上图片中有2个标号
1.按照途中标号1内的选项设置
2.确定应用

3.2 session序列化存储

Idea会在Tomcat服务器退出内存后自动清除session序列化内容,需要额外设置开启允许存储序列化内容


以上图片中有2个标号
1.点击选中
2.确定应用

3.3 默认serlvet(Root)入口

Idea中Tomcat部署路径不是Tomcat包中的Webapps下,而是用了Tomcat实例在以下图片所示路径中


因此无法直接访问到默认入口,需要额外设置


以上图片中有2个标号
1.点击选中
2.确定应用

final关键字修饰形参的应用场景

发表于 2018-04-19 | 分类于 Tips

作者: insightcodeyk
出处: https://insightcodeyk.github.io/2018/04/19/FinalKeywordInPara/
声明: 本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。

关键字: 成员方法中的内部类 局部变量 常量的生命周期

这个问题的答案来自:
StackOverFlow Michael Borgwardt
final参数的应用场景:
若该参数在传入成员方法的内部类中被使用,若不设置为final类型,则可能发生成员方法以及该临时变量参数引用被回收而内部类没被回收,内部类访问不存在的地址.

Iterator<Integer> createIntegerIterator(final int from, final int to) {
    return new Iterator<Integer>() {
        int index = from;
        public Integer next() {
            return index++;
        }

        public boolean hasNext() {
            return index <= to;
        }
        // remove method omitted
    };
}

在这个例子中,传入参数from, to在匿名内部类中被使用,改类中的方法调用的时候,这两个参数若不为final,则其引用在这个createIntegerIterator方法执行完之后就会被回收.
也就是说,成员方法内部类的生命周期长于成员方法本身,因此,内部类使用的成员方法中定义的变量生命周期必须等于或长于内部类,final变量就是很好的解决方案。
需要特别注意的是,现在即便这个参数不声明为final也可以运行,原因是因为,编译器帮我做了这件事,当我们看查反编译的字节码文件时,可以发现,编译器帮我们补上了final关键字.

Tomcat中遇到的相对/绝对路径问题

发表于 2018-04-12 | 分类于 Tomcat

作者: insightcodeyk
出处: https://insightcodeyk.github.io/2018/04/12/AbsoluteAndRelativePathInTomcat/
声明: 本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。

1 form表单中action属性

1.1 绝对路径

<form action="https://www.baidu.com/"></form>
url的完全表达方式(在tomcat中就是某个虚拟主机下的某个资源的完全路径)

1.2 相对路径a

<form action="/foo"></form>
若如上所述,uri以"/"开头,则相对路径的锚就是:服务器根目录(在Tomcat中就是虚拟主机域名+端口号)

1.3 相对路径b

<form action="foo"></form>
若如上所述,uri不以"/"开头,则相对路径的锚就是:当前目录(页面)的父目录

特别注意:若是想通过action调用servlet,则这种方法只有页面位于WEB-INF下才有意义,
这句话等价于,只有当前页面的父目录是当前web应用的根目录时才有意义。
因为若当前目录的父目录不是web的根目录,则无法通过这个锚访问到任何servlet(servlet在Tomcat中是以web根目录为锚)。
因此不推荐使用这种相对路径

2 Tomcat中的重定向sendRedirect()

2.1 绝对路径

response.sendRedirect("https://www.baidu.com/");
如上所述,和form表单的绝对路径含义完全相同

2.2 相对路径a

response.sendRedirect("https://www.baidu.com/");
如上所述,以"/"开头的相对路径方法,是以服务器为锚,也就是和form表单相同

2.3 相对路径b

response.sendRedirect("/servlet/redirect");
如上所述,不以"/"开头的相对路径方法,是以当前目录(页面)的父目录为锚,也和form表单相同

3 Tomcat中的转发器getRequestDispatcher()

重点声明(容易混淆的):
转发器不能向当前web应用外请求转发,所以在转发器中的绝对路径概念和以上两个内容的绝对路径含义不同

3.1 绝对路径

request.getRequestDispatcher("/servlet/b");
以"/"开头,表示以当前web应用根目录为锚(在Tomcat中就是以web应用所在目录为锚)

3.2 相对路径

request.getRequestDispatcher("servlet/b");
不以"/"开头,表示以当前目录(网页)的父目录为锚

关于多线程中两个缓存包装流同时包装一个节点流的问题

发表于 2018-04-01 | 分类于 Tips

作者: insightcodeyk
出处: https://insightcodeyk.github.io/2018/04/01/StreamInPackagingAndNode/
声明: 本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。

这个问题起源于对多线程文件上传的测试。在这个测试中,Server和Client通过TCP连接,Client请求向Server上传文件,Server接受请求后,把Client上传的文件存入指定位置。这原本是个非常简单的问题,但是在这个过程中出现了一个bug,在某些时候会出现文件上传不完整的问题。在多次排查后,发现问题的源头:
这里用了两个包装流一个是BufferedWriter用于获取文件名称,一个是BufferedInputStream用于获取文件内容,这两个包装流同时从socket获得OutputStream:

//这是获取文件名称的流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sos));
bw.write(file.getName());
bw.newLine();

//这是获取文件内容的流
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(sos);

//两个流先后共用了从Client的Socket获得的OutputStream

在排查过程中发现在某些时候Server从bis中无法读到任何字节,从而丢失了部分上传内容。关于两个包装流先后使用一个节点流的情况JDK没有进行描述,google和baidu都没有类似的问题提出,因此只能看BufferedInputStream和BufferedWriter的源码,最后这个问题并没有在源码中获得很好的解释,因为read0()是一个native方法,无法得知JDK读取流中一个字节的具体处理过程。只要保证不要在开发过程中对同一个节点流使用不同包装流即可以避免这个问题。

容器特点和应用场景

发表于 2018-03-27 | 分类于 JavaSummery

作者: insightcodeyk
出处: https://insightcodeyk.github.io/2018/03/27/ContainerFeatures/
声明: 本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。

1 数组 和 Arrays

谈到Collection和Map之前,首先要提到数组。数组是一种高效的存储和访问对象的逻辑结构顺序表的实现,以上是它的优点。而它的缺点在于其容量固定无法动态改变,且无法直接知道当前数组中实际元素个数。
对数组的操作JDK提供了工具类Arrays,其中包含了很多易用的方法。

2 Collection 和 Map

为了解决数组容量无法动态增加的情况,引入了容器,并扩展出了其他一些功能。

2.1 Collection 和 Map 的区别

重点区别在于:元素结构
Collection中元素是一个值
Map中元素是一个键值对

2.2 Collection 和 Map 子类特征

Collection:

List:维护有序集合(有序的含义是插入顺序)
Set:集合内无重复元素

Map:

HashMap:基于哈希表的Map,无序
TreeMap:基于红黑树的Map,有序

3 默认优先选择

3.1 List结构

默认选择: ArrayList
插入删除频繁: LinkedList
作为多线程公共资源: Vector(开发中不推荐用,资源消耗太大,多线程问题是另一问题,在concurrent包或者Collections工具类中通过其他方式实现)

3.2 Set结构

默认选择: HashSet(由HashMap,value指向一个静态Object类常量PRESENT)
对数据顺序有要求(不是插入顺序): TreeSet

3.2 Map结构

默认选择: HashMap
对数据顺序有要求(不是插入顺序): TreeMap

4 容器框架常用api和注意事项

https://insightcodeyk.github.io/2018/03/12/Container/

一些关于HashMap的思考

发表于 2018-03-20 | 分类于 Tips

作者: insightcodeyk
出处: https://insightcodeyk.github.io/
声明: 本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。

关于HashMap的详细解析请阅读以下文章

HashMap机制与要点

1 HashMap中的”相等”语义

JDK1.8中HashMap判断两个key是否相等的条件是:

if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))

可以看出首先判断hash值是否相等,再判断内存地址是否相等或者equals方法是否为true。在没有覆盖equals的情况下,第二个条件需要内存地址相同,也就是相同的引用才能判true。多数情况下我们的业务场景需要根据成员变量的值来覆盖equals()方法获得自己定义的”相等”语义。

2 Key和不可变类型

维护Key值得不可变性,也就是说保持(设定)Key类型为不可变类型是非常重要的。在开发过程中常用见的情况是选择主键和常量作为equals方法和hashCode方法的参数。为了减少后来者更改代码可能带来的风险,良好的习惯是对作为参数的成员变量全部设置为final。通过不提供set方法
来保证安全不是推荐的方式。
若Key类型为可变类型在某些情况下会带来的风险就像以下测试代码中展示的:
代码导读:
1.以下class VariedKey是Key的类
2.public类是HashMapTest,以下只展示main方法,HashMapTest类中只有main方法
3.重点部分解释在main方法的代码注释中

    /*  
        这是Key对象的类,其中覆盖了hashCode()和equals(),用两个成员变量作为参数。其中constantKey作为final变量,而variedKey变量可以通过setVariedKey方法进行更改。

    */
    class VariedKey{
    private fianl int constantKey; 
    private int variedKey;

    public VariedKey(int constantKey, int variedKey) {
        this.constantKey = constantKey;
        this.variedKey = variedKey;
    }

    public void setVariedKey(int setKey) {
        variedKey = setKey;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        VariedKey variedKey1 = (VariedKey) o;
        return constantKey == variedKey1.constantKey &&
                variedKey == variedKey1.variedKey;
    }

    @Override
    public int hashCode() {

        return Objects.hash(constantKey, variedKey);
    }
}

main方法:

public static void main(String[] args) {
    //这部分是构建Map
    VariedKey mapElm1 = new VariedKey(1, 2);
    VariedKey mapElm2 = new VariedKey(3, 4);
    VariedKey mapElm3 = new VariedKey(5, 6);
    VariedKey mapElm4 = new VariedKey(7, 8);
    HashMap<VariedKey, String> temMap = new HashMap<>();
    temMap.put(mapElm1, "a");
    temMap.put(mapElm2, "b");
    temMap.put(mapElm3, "c");
    temMap.put(mapElm4, "d");


    Set<VariedKey> keySet = temMap.keySet();
    for(VariedKey e : keySet) {
        System.out.println(e.hashCode() + " : " + temMap.get(e));
    }

    System.out.println();

    mapElm1.setVariedKey(5); //!!!这句更改了第一个元素的variedKey成员变量,变成(1, 5)
    VariedKey mapElem5 = new VariedKey(1, 5);//在这里再put一个成员变量为(1, 5)的元素进Map
    temMap.put(mapElem5, "e");
    System.out.println("SizeMap: " + temMap.size());//Map的值从4变成了5

    //实际上,通过修改该第一个元素的成员变量,我们已经无法再访问到第一个元素放入的value值

    keySet = temMap.keySet();
    System.out.println("SizeKeySet: " + keySet.size());
    System.out.println();
    for(VariedKey e : keySet) {
        System.out.println(e.hashCode() + " : " + temMap.get(e));
    }

    VariedKey mapElm6 = new VariedKey(1, 2);//当我们重新创建一个变量和第一个元素更改前的Key


    System.out.println();
    System.out.println(mapElm6.hashCode() + " : " + temMap.get(mapElm6));//当我们用这个Key去获得value值时,value值为null

    }

结论:对于HashMap的Key元素类型设定一定要保证为不可变类型,最好设置为final以保证后续维护人员清楚这是不可更改的类型。

容器

发表于 2018-03-12 | 分类于 JavaSummery

作者: insightcodeyk
出处: https://insightcodeyk.github.io/2018/03/12/Container/
声明: 本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。

1 Collection<E>接口

集合类型的根接口

总共有15种方法,记录其中常用的9个
分别是,
boolean add(E e); 向集合中添加元素e
boolean addAll(Collection<? extends E> c); 向集合中添加另一个集合中的全部元素,参数必须是同类或子类
void clear(); 移除集合众所有元素
boolean contains(Object o); 查找指定元素
boolean containsAll(Conllection<?> c) 判断集合c是否是该集合的子集
boolean isEmpty(); 判断集合是否为空
Iterator iterator(); 获取该集合的迭代器
boolean remove(Object o); 从集合众删除指定元素
int size(); 返回集合中元素个数

1.1 List<E>接口

有序集合接口,继承自Collcetion,可实现随机查找,List判断相等用Object.equals()方法

记录其不同于Collcetion的部分方法,
分别是,
boolean add(E e); 向列表尾部添加指定元素
void add(int index, E element); 在列表的指定位置插入指定元素
E get(int index); 获取列表中指定位置的元素
int indexOf(Object o); 获取指定元素的第一个索引位
int lastIndexOf(Object o); 获取指定元素的最后一个索引位
ListIterator listIterator(int index); 返回列表的迭代器,从index位置开始
E set(int index, E element); 用指定元素替代Index位置的元素

1.1.1 ArrayList<E>类

实现动态数组效果的List子类,线程不安全(不同步)

总共3个构造方法,
分别是,
ArrayList(); 构造初始容量为10的空列表
ArrayList(Collection<? extens E> c); 用指定列表c构造新列表
ArrayList(int initialCapacity); 构造指定初始容量initialCapacity的空列表

不同于其父类接口的主要方法:
void trimToSize(); 将ArrayList容量调整为列表当前大小

1.1.2 Vector<E>类

实现动态数组,和ArrayList类在大部分情况下拥有相同的方法,重要区别在于,线程安全(同步)

1.1.3 LinkedList<E>类

List接口的链表实现,插入删除效率较高,方法没有太多区别

1.1.4 Stack<E>类

实现栈结构

总共有5个方法
分别是,
boolean empty(); 测试是否栈空
E peek(); 查看栈顶元素
E POP(); 出栈
E push(E itme); 压栈
int search(Object); 返回对象在栈中位置

1.2 Set<E>接口

特点:不包含重复元素的集合,重复的定义:e1.equals(e2),这意味着当我们重写了equals方法Set将会接受不同的重复规则.

1.2.1 HashSet<E>类

用HashMap实现的Set,特点是不维护序列,优点是有非常高的查找效率

1.2.2 TreeSet<E>类

用红黑树实现的Set,特点是维护了序列

1.2.3 LinkedHashSet<E>类

一个折中的Set,维护了序列,也用了Hash

1.3 Queue<E>接口

实现队列操作的接口,其子类包括队列的常用操作方法

boolean add(E e); 也就是队列的入队操作
E element(); 获取但不出队操作
E poll(); 也就是出队操作,队列为空则返回nul

2 Map<K,V>接口

键值对类型的集合,小数据库

2.1 AbstractMap<K,V>类

提供Map接口的主要功能的实现

主要记录部分方法,
分别是,
V get(Object key); 返回指定键所映射的值;如果没有不包含键的映射关系,返回bull。是否允许Key值为null,可以在具体使用的子类中控制
Set keySet(); 获得Map中包含的键值集合
V put(K key, V value); 向Map中添加一对新的键值对关系,若该键已经存在,则更新该键值对
V remove(Object key); 若存在该键,则从Map中删除该键值对,如果没有则返回null(在不允许null的Map中)

2.1.1 HashMap类

基于哈希表的Map实现类,使用最为频繁的Map,允许使用null作为键和值.

总共有4个构造函数
分别是,
HashMap(); 默认初始容量16,默认加载因子0.75,一般不改加载因子
HashMap(int initialCapacity); 指定初始容量
HashMap(int initialCapacity, float loadFactor); 指定初始容量和加载因子
HashMap(Map<? extends K, ? extends V> m); 用m构造相同的Map
成员方法和其父类接口相同

2.1.2 TreeMap类

维护了一个顺序序列的集合,也就是说当用Iterator迭代器获取Map.Entry对象迭代的时候,迭代顺序是自然顺序或者Comparable顺序

流

发表于 2018-03-10 | 分类于 JavaSummery

作者: insightcodeyk
出处: https://insightcodeyk.github.io/2018/03/10/Stream/
声明: 本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。

概述

流是一组有序的数据序列,有两种分类方式:

根据操作的类型,分为 输入流 和 输出流
根据传输内容类型,分为 字节流 和 字符流

1 文件流

1.1 字节流

以字节为传输单位的流

1.1.1 字节输入流

字节输入流超类(抽象):InputStream

总共包含9个方法,其中有3个read方法(重点: 在输入数据可用、检测到文件末尾或者抛出异常之前,read方法阻塞)
分别是,
abstract int read(); 从输入流读取一个字节,返回该字节的十进制编码,结束返回-1
int read(byte[] buffer); 从输入流读取一个字节数组,返回数组中获得的字节数,结束返回-1
read方法自动检测byte[]数组长度,每次读入最多(length - 1)个字节
int read(byte[] buffer, int offser, int len); 从输入流读取一个字符数组,指定开始>和长度,结束返回 -1

1.1.1.1 字节文件输入流FileInputStream

构造方法总共三个,记录其中两个
分别是,
FileInputStream(File file); 通过一个文件对象创建字节文件流
FileInputStream(String name); 通过文件路径名创建字节文件流

1.1.1.2 字节缓冲文件输入流BufferedInputStream

建立在InputStream上,通过提供流内缓冲数组来加快传输速度.

构造方法总共两个
分别是,
BufferedInputStream(InputStream in); 在InputStream的基础上创建创建字节缓冲文件输入流
BufferedInputStream(InputStream, int size); 与以上构造方法的区别在于, size指定了流缓冲区域的大小

1.1.2 字节输出流

字节输出流超类(抽象):OutputStream

总共包含5个方法,其中有3个write方法
分别是,
absract void write(int b); 向输出流写入一个字节
void write(byte[] b); 向输出流写入b数组中从0开始b.length个字节的数据
void write(byte[] b, int off, int len); 向输出流中写入b数组中从off开始的len个字节的数据

1.1.2.1 字节文件输出流FileOutputStream

构造方法总共5个,记录4个
分别是,
FileOutputStream(File file); 通过文件对象创建字节文件输出流(每次创建清空该文件)
FileOutputStream(File file, boolean append); 同上,区别在于若append == ture,则不清空文件,并从文件末尾添加
FileOutputStream(String name); 通过文件路径名创建字节文件输出流(每次创建清空该文件)
FileOutputStream(String name, boolean append);同上,区别在于若append == ture,则不清空文件,并从文件末尾添加

1.1.2.2 字节缓冲文件输出流BufferedOutputStream

建立在OutputStream上(组合关系)的缓冲流,使用的直接目的是传输速度显著提高
重点注意之一,所有的Bufered*输出流,在调用写方法后,需要在适时的时候刷新,需要控制刷新防止最后部分数据没有被写出.

构造方法有两个
分别是,
BufferedOutputStream(OutputStream out); 创建一个缓冲输出流
BufferedOutputStream(OutputStream out, int size); 创建一个指定缓冲区大小的缓冲输出流

1.2 字符流

以字符为传输单位的流

1.2.1 字符输入流

字符输入流超类(抽象):Reader

总共有10个方法,其中read方法有4个
分别是,
abstract int read(char[] cbuf, int off, int len);将len个字符读入数组cbuff,从off开始,读到流末尾返回-1
int read();返回读取到的一个字符值,读到流末尾返回-1
int read(char[] cbuf);将字符读入数组,读入长度为cbuf.length,读到流末尾返回-1
int read(CharBuff target);试图将字符读入指定的字符缓冲区。缓冲区可照原样用作字符的存储库:所做的唯一改变是put操作的结果。不对缓冲区执行翻转或重绕操作。(没用过,复制过来jdk1.6)

1.2.1.1 字符输入流转换器InputStreamReader

将字节流转换为字符流

有4个构造方法,记录其中2个
分别是,
InputStreamReader(InputStream in); 用字节流创建默认字符集的字符流
InputStreamReader(InputStream in, String charseName); 用字节流创建指定字符集的字符流

1.2.1.2 便捷文件字符输入流FileReader

使用默认字符集构造字符输入流

总共3个构造方法,记录其中两个
分别是,
FileReader(File file); 给定文件对象创建便捷文件字符输入流
FileReader(String fileName); 给定文件路径名创建便捷文件字符输入流

1.2.1.3 缓冲字符输入流BufferedReader

Buffered:作用同上,显著提高传输速度.
重点注意之一,所有的Bufered*输出流,在调用写方法后,需要在适时的时候刷新,需要控制刷新防止最后部分数据没有被写出.
同时与以上输入流方法相比多了一个方法:
public String readLin(); 读取一行文本。以’\r’, ‘\n’, ‘\r\n’为结束标志.

总共两个构造方法,
分别是,
BufferedReader(Reader in); 创建一个使用默认大小输入缓冲区的缓冲字符输入流
BufferedReader(Reader in, int sz); 指定缓冲去大小,其他同上一个方法相同

1.2.2 字符输出流

字符输出流超类(抽象):Writer

1.2.2.1 字符输出流转换器OutputStreamWriter

将字节流转换为字符流

有4个构造方法,记录其中2个
分别是,
OutputStreamWriter(OutputStream out); 用默认字符集字节流创建字符输出流
OutputStreamWriter(OutputSteam out, String charsetName); 指定字符集,用字节流创建字符流

1.2.2.2 便捷文件字符输出流FileWriter

所谓便捷指的是创建便捷,不用自己指定字符集以及缓冲区(流缓冲区)

总共有5个构造方法,记录其中4个
分别是,
FileWriter(File file); 通过文件对象构造(首次写出,清空对应文件)
FileWriter(File file, boolean append); 与上区别在于,append若为true,则向文件末尾开始添加
FileWirter(String fileName);通过文件路径名构造,其他相同
FileWirter(String fileNmae, boolean append);通过文件路径名构造,其他相同

1.2.2.3 缓冲字符输出流BufferedWriter

缓冲的作用和上文相同

总共2个构造方法,
分别是
BufferedWriter(Writer out); 使用默认大小的输出缓冲区的缓冲字符输出流
BufferedWriter(Writer out, int sz); 同上的区别在于:使用给定大小的输出缓冲区
与其他写方法类的方法不同在于:
void newLine(); 写入一个行分隔符(自动配饰操作系统)

2 特殊流

2.1 “标准”输入输出流System.in/System.out

public static final InputStream in;
(System.in) “标准”输入流,常用于键盘输入.
public staic final PrintSteam out; (System.out) “标准”输出流,常用于显示器输出.
(标准是引号的原因未知,jdk API1.7:”standard”)

2.2 打印字节流PrintStream/打印字符流PrintWrite

重点区别之一,打印流可以在构造的时候指定开启自动刷新.
PrintStream: 包装器,为其他输出流添加功能,使其能方便的打印各种数据值表示形式
PrintWrite: 实现类似C语言中的printf()函数的输出控制效果

2.3 序列化流ObjectOutputStream/ObjectInputStream

序列化流,用于保存和读入类信息,static 和 transient 声明的变量不被保存

3 其他流

4 特别注释

文件类File

创建管理文件以及目录,包含4种构造方法和非常多个成员方法

PCA

发表于 2016-04-12 | 分类于 Pattern Recognition

作者: insightcodeyk
出处: https://insightcodeyk.github.io/
声明: 本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。

1 PCA算法

PCA是一种运用K-L变换的统计分析方法,其实质是将多指标化为几个综合指标。在图像处理问题中,PCA的思想得到了很好的运用,图像多位高维数据,其样本分布规律复杂,数据难以处理,运用PCA的思想,找出几个综合变量替代原来大量且复杂的变量,可以极大的降低运算难度和处理难度。

1.1 K-L变换原理

K-L是一种最优正交变换,这一变换有去相关性的突出优点,因此可以去掉一些带有较少信息的维度,达到降维目的,其原理如下。
假设Y为N维随机变量,Y可以表示为n个基向量的加权和:

其中 为加权系数, 为基向量。
设随机向量的总体自相关矩阵为:

最终可得到:

其中 是本征向量。又因为R为实对称矩阵,所以其不同本征值对应的本征向量正交。

1.2 奇异值分解定理(SVD)

在多数情况下所获得的人耳图像并不是方阵形式,行列向量并不相等,这时候就需要用到奇异值分解。对于这种mn形式的矩阵,奇异值分解法的分解形式如下:

其中Am
n是一个mn的矩阵,Umm是一个被称为做奇异向量的mm方阵,Σmn是一个对角线上数值为奇异值的对角矩阵,VnnT是为称为右奇异向量的nn方阵。
将人耳图像的奇异值作为特征向量是有优点的:
1.图像的奇异值较为稳定,因此当有外界干扰时,奇异值基本不变。
2.奇异值对图像的各种变化,如平移、旋转、伸缩、灰度值比率的变化有较好的鲁棒性。

12

Ke Yi

Le vent se lève ,il faut tenter de vivre

11 日志
5 分类
6 标签
GitHub E-Mail
© 2018 Ke Yi
由 Hexo 强力驱动 v3.7.1
|
主题 — NexT.Gemini v6.1.0