集合排序==>利用Collections工具类


集合提供了一个工具类:java.util.Collections
集合的工具类提供了若干静态方法,可以方便我们对集合做一系列操作,其中之一就是排序.
需要注意,提供的方法只能对List集合排序,因为set集合不全是有序的..

Random rand = new Random();
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 10; i++) {
    list.add(rand.nextInt(100));
}
System.out.println("未排序的List集合"+list);
Collections.sort(list);
System.out.println("排序后的List集合:" + list);


自定义类型的排序


自定义的类进行排序必须实现Comparable接口,重写compareTo()方法

/**自定义Point类*/
public class Point implements Comparable<Point>{
    private int x;
    private int y;
    /*空参构造,有参构造,set,get构造器,equals,toString*/
    /**
     * 当一个类实现了Comparable接口后,要求必须实现compareTo()方法.
     * 该方法的作用是定义当前对象this于参数对象o之间的大小关系.
     * 
     * 返回值是一个int值,该值不关系具体取值,只关注取值范围.
     * 当:
     * 返回值>0;当前对象大于参数对象;
     * 返回值<0;当前对象小于参数对象;
     * 返回值=0;两对象相等;
     */
    @Override
    public int compareTo(Point point){
        /*
         * 两点比较大小的方式:点到原点的距离长的大
         */
        int len = this.x * this.x + this.y * this.y;
        int pointLen = point.x * point.x + point.y * point.y;
        return len -pointLen;
    }
}

/**排序程序*/
public static void main(String[] args) {
    Random rand = new Random(); 
    List<Point> list = new ArrayList<Point>();
    for (int i = 0; i < 6; i++) {
        list.add(new Point(rand.nextInt(20), rand.nextInt(20)));
    }
    System.out.println("未排序的List<Point>集合:  "+list);
    /**
     * Collections.sort(List list)
     * 该方法排序给定集合时对集合元素有一个要求,就是元素必须实现了Comparable接口,否则编译不通过.
     */
    Collections.sort(list);
    System.out.println("自定义排序的List<Point>集合:  "+list);
}

**********************************************************************************
侵入性


Collections提供了一个重载的sort()方法,可以允许传入一个额外的比较器,按照该比较器的规则对元素比较大小后进行排序
1:排序方式可以自定,更灵活
2:不要求元素实现Comparable接口,没有侵入性

Collections.sort(list,new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }
});


Queue队列

*java.util.Queue
线性表,一端进offer,一端出poll
原则FIFO:先进先出
Queue接口继承自Collection.队列可以保存一组元素,但是存取元素必须遵循先进先出的原则.
常用的实现类:java.util.LinkedList
由于链表可以保存一组元素,并且首位增删元素效率高,它满足队列相关操作特性.

Queue<String> queue = new LinkedList<String>();
//当作队列看,则调用队列方法
/*
 * offer():入队操作,将元素添加到队列末尾
 */
queue.offer("one");
queue.offer("two");
queue.offer("three");
queue.offer("four");
queue.offer("five");
System.out.println(queue);
System.out.println("queue的大小" + queue.size());
/*
 * poll():出队操作
 * 获取对手元素后,该元素即从队列中被移除
 */
queue.poll();//one
System.out.println(queue);
/*
 * E peek()
 * 引用队首元素,获取队首元素后,该元素任然保留在队列中
 */
String str = queue.peek();
System.out.println("str=" + str);
System.out.println(queue);
Queue的遍历操作
    for (String s : queue) {
        System.out.println(s);
    }


双端队列Deque


java.util.Deque接口
双端队列,是Deque的子接口,双端队列的特点是:对列两端都可以做进出队列的操作,
常用的实现类:java.util.LinkedList

Deque<String> deque = new LinkedList<String>();
deque.offer("one");
deque.offer("two");
deque.offer("three");
System.out.println(deque);//[one, two, three]
//队首入队
deque.offerFirst("four");
System.out.println(deque);//[four, one, two, three]
//队尾入队
deque.offerLast("five");
System.out.println(deque);//[four, one, two, three, five]

deque.poll();
System.out.println(deque);//[one, two, three, five]

//队首取元素
deque.pollFirst();
System.out.println(deque);////[two, three, five]

//队尾取元素
deque.pollLast();
System.out.println(deque);//[two, three]


栈Stack===>Windows的前进后退功能


栈也可以存放一组元素,但是存取元素必须遵守:先进后出原则FILO
双端队列如果只从同一段做进出队操作时,就实现了栈操作.
为此双端队列Deque为栈提供了对应的方法
入栈操作:push
出栈操作:pop

Deque<String> stack = new LinkedList<>();
//push入栈操作--offerFirst
stack.push("one");
stack.push("two");
stack.push("three");
System.out.println(stack);//[three, two, one]
//pop出栈操作--pollFirst
stack.pop();//three
System.out.println(stack);//[two, one]


集合的并发安全问题Collections.synchronizedList()/synchronizedSet()


集合的工具类Collections可以将现有的集合转换为一个线程安全的,对于集合自身的操作如:add, remove等都是可以保证并发安全的.
但是API手册也有说明,一个并发安全的集合也不与使用迭代器遍历操作做互斥,这意味着并发操作中遍历与集合自身操作并发并发安全的,需要自行维护互斥关系.

/*
 * 集合的工具类Collections可以将现有的集合转换为一个线程安全的,对于集合自身的操作如:add,
 * remove等都是可以保证并发安全的.
 * 但是API手册也有说明,一个并发安全的集合也不与使用迭代器遍历操作做互斥,这意味着并发操作中
 * 遍历与集合自身操作并发并发安全的,需要自行维护互斥关系.
 */
/*
 * List的常用实现类:ArrayList,LinkedList都不是线程安全的
 * 可以通过Collections将List转换为线程安全的.
 */
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
System.out.println(list);
/*
 * Collections.synchronizedList()
 * 转换线程安全
 */
list = Collections.synchronizedList(list);
//HashSet也不是线程安全的
Set<String> set = new HashSet<>(list);
set = Collections.synchronizedSet(set);
System.out.println(set);
阻塞队列BlockingQueue和BlockingDeque(双端)
阻塞队列时并发安全的队列,并且内部由双队列实现双缓冲操作,存取无互斥,提高并发效率

BlockingQueue<String> queue = new LinkedBlockingQueue<String>();
queue.offer("one");
queue.offer("two1");
queue.offer("two2");
queue.offer("three");
try {
    queue.offer("five", 5, TimeUnit.MINUTES);
} catch (InterruptedException e) {
    e.printStackTrace();
}



HTTP协议 --- 超文本传输协议


HTPP协议是应用层协议,在互联网上广泛被使用,BS结构通讯的基础协议.浏览器与服务端之间就是使用HTTP协议交互数据的.而HTTP协议要求必须建立在可靠的传输协议基础之上进行,所以底层的通讯协议通常使用TCP协议.

HTTP协议规定了客户端(浏览器)与服务端之间传输的数据的格式及内容,并且也规定了两者之间的通讯方式.

在HTTP协议中要求的通讯方式为:一次请求一次响应,即:

客户端主动连接服务端并发起请求(Request),服务端在收到请求后给予响应(Response).服务端永远不会主动连接客户端,也不会主动发送任何相应.
HTTP常见的两个版本1.0和1.1,现在基本上都是使用HTTP1.1协议.1.0和1.1协议有一个比较明显的改动:
1.0协议在通讯时,客户端与服务端建立一次TCP链接后,发送一次请求,当服务端处理并给予后即断开连接.
1.1协议则是在建立一次TCP连接后,可以经历多次请求与相应的过程后再断开连接. 在当今互联网应用中个,这样的做法可以减少不小的系统开销并提高相应效率.
HTTP协议中所有的字符信息使用的是字符集ISO8859-1,该字符集是一个欧洲编码集, 里面不含有中文等字符信息,所有HTTP协议中的字符信息部分不得出现如中文这样的 字符,一般只用字母,数字,符号.

HTTP请求定义(Request)

一个请求应当包含三个部分:请求行+消息头+消息正文

1:请求行:请求行是由一行字符串组成的(以CRLF两个符号结尾表示一行结束)
            格式:
            method     url       protocol(CRLF)
            请求方式 请求资源路径 协议版本

            注:CR,LF是两个不可见符号,在ASCII编码中对应的数组为13,10
            CR:回车符   LF:换行符

            请求的两种方式method:
            GET:地址栏请求,用户若传递数据则是直接平在资源路径中
            POST:将用户传递的数据包含在消息正文中传递
2:消息头:消息头是由若干行构成,每一行为一个消息头.消息头是客户端通讯过程中传递给服务端的一些附加消息,比如有的用来告知服务端客户端的基本情况(浏览器内核,操作系统信息等),有的用来维护通讯过程中的必要信息,有的是用来说明是否包含消息正文以及正文内容以及长度.
            格式:
            name:value(CRLF)
            在最后一个消息头后面会单独跟一个CRLF,表示消息头部分结束.

            例如:
            Host: localhost:8088(CRLF)
            Connection: keep-alive(CRLF)
            Upgrade-Insecure-Requests: 1(CRLF)
            User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36(CRLF)
            Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8(CRLF)
            Accept-Encoding: gzip, deflate, br(CRLF)
            Accept-Language: zh-CN,zh;q=0.9(CRLF)(CRLF)
3:消息正文:一个请求中可以不包含消息正文部分,消息正文是2进制数据,是用户提交给服务端的数据. 具体消息正文有多少个字节,这些字节表示的是什么类型的数据可以根据该请求中的两个消息头: Content-Type与Content-Length得知.
响应(Response)
响应时服务端发送给客户端的内容,HTTP协议对应响应的格式有所规定。 一个响应也含有三个部分:状态行,响应头,响应正文

1:状态行
状态行也是有一行字符串构成的(以CRLF结尾)
    格式:
    protocol status_code status_reason(CRLF)
    协议版本        状态代码                 状态描述
状态代码是由一个三位数字组成,不同的状态码时用来告知客户端服务端对此请求的处理结果。它分为5类:

1XX:1.0协议是为保留部分,没有启用
2XX:成功
3XX:重定向
4XX:客户端错误
5XX:服务端错误

常见的状态码:
200:请求处理成功,正常响应客户端
302:要求客户端重定向到指定地址
404:客户端请求错误,服务端无法处理该请求
500:服务端处理请求时发生了错误
Status-Code    = "200"   ; OK
              | "201"   ; Created
              | "202"   ; Accepted
              | "204"   ; No Content
              | "301"   ; Moved Permanently
              | "302"   ; Moved Temporarily
              | "304"   ; Not Modified
              | "400"   ; Bad Request
              | "401"   ; Unauthorized
              | "403"   ; Forbidden
              | "404"   ; Not Found
              | "500"   ; Internal Server Error
              | "501"   ; Not Implemented
              | "502"   ; Bad Gateway
              | "503"   ; Service Unavailable
响应头

*响应头的格式与请求中的消息头是一样,由若干行组成,每行的格式:
name: value(CRLF)
并且最后一个响应头发送完毕后会单独发送一个CRLF表示响应头部分发送完毕。响应头是服务端发送给客户端的附加消息.

响应正文

响应正文也是二进制数,是服务端响应客户端锁清秋的资源数据。
BufferedRead-- \r\结尾,但是这个不是,所以不能用高级流


Map查找表


java.util.map 接口
Map 查找表
Map结构的样子就是一个多行两列的表格,左列称为Key,右列称为Value,Map总是以键值对的形式保存一组数组.并且获取信息是根据key查找对应的value.
常用的实现类:HashMap.散列表,使用散列算法实现的Map,当今查询速度最快的数据结构

Map<String, Integer> map = new HashMap<String, Integer>();
//Map<String, Integer> map = new LinkedHashMap<String,Integer>();//LinkedHashMap可以按照怎么存怎么取
/**
 * V put<K k,V v>
 * 像当前Map中存放指定的key-value对.
 * Map有一个要求,key不允许重复,是否重复也是依靠key滋生的equals比较的结果.
 * 如果以Map中已有的key存入key-value对,则是替换value操作.那么返回值为被替换的value,否则是null
 */
/*
 * 如果value是包装类,那么注意,接收put方法返回值时要使用包装类接收,切记不可以使用
 * 基本类型接收,因为编译器会自动添加自动拆箱操作,如果put方法的返回值为null,那么就会报NullPointException
 */
System.out.println("************************增********************");
Integer old = map.put("语文", 98);
System.out.println(old);
map.put("数学", 100);
map.put("物理", 99);
map.put("化学", 97);
map.put("英语", 95);
System.out.println(map);
Integer put = map.put("化学", 1000);
System.out.println("put="+put);
System.out.println(map);

System.out.println("********************元素个数********************");
/**
 * 获取元素个数(key-value一组键值对算一个元素)
 */
int size = map.size();
System.out.println("size=" + size);

System.out.println("**********************取**********************");
/**
 * 获取元素
 * V get(Object k)
 * 根据给定的key获取对应的value
 * 若给定的key再map中不存在,则返回值为null
 */
Integer score = map.get("语文");
System.out.println("语文value:" + score);
score = map.get("体育");
System.out.println("体育value:" + score);

System.out.println("*********************删***********************");
/**
 * 删除元素
 * V remove(K k)
 * 根据给定的key删除对应的一组键值对,返回值为该key所对应的value
 */
Integer remove = map.remove("化学");
System.out.println("remove化学:" + remove);
System.out.println(map);
Map也提供了对应的contains方法,并且可以分别判断包含key和value

boolean containsKey(K k)
boolean containsValue(V v)

/*
 * key和value的包含关系也是依靠元素自身的equals方法比较的结果而定
 */
boolean b = map.containsKey("物理");
System.out.println("物理是否包含:" + b);
b = map.containsValue(96);
System.out.println("96分是否包含;" + b);
Map的遍历操作
遍历一个Map有三种方式
遍历所有的key
遍历所有的key-value对
遍历所有的value(相对不常用)
    Map<String, Integer> map = new HashMap<String,Integer>();
    map.put("历史", 86);
    map.put("数学", 100);
    map.put("物理", 99);
    map.put("化学", 97);
    map.put("英语", 95);
    System.out.println(map);

    /*
     * 遍历所有的key
     * Set<K> ketSet()
     * 该方法将当前Map中所有的元素以一个Set集合形式返回,遍历该集合就等同于遍历了所有的key
     */
    Set<String> keySet = map.keySet();//key是不重复的,而set集合就是不重复集合
    System.out.println("******************for each遍历*******************");
    for (String key : keySet) {
        System.out.println(key);
    }
    System.out.println("******************Iterator遍历*******************");
    Iterator<String> it = keySet.iterator();
    while(it.hasNext()){
        System.out.println(it.next());
    }

    /*
     * 遍历所有的key-value对
     * Set<Entry> entrySet()
     * 该方法将当前Map集合中每一组键值对(若干Entry实例)以一个Set集合的形式放回
     * 
     * java.util.Map.Entry
     * Entry是Map的一个内部接口,其实现类的每一个实例用于表示当前Map的一组键值对.该接口规定了获取其表示的这组键值对的key和value的方法.
     * 
     * K getKey()
     * V getValue()
     * 不同的Map都实现了Entry,并用实现类实例表示其每一组键值对.HashMap也一样
     */
    System.out.println("******************遍历所有的key-value*******************");
    Set<Entry<String, Integer>> entrySet = map.entrySet();
    for (Entry<String, Integer> entry : entrySet) {
        System.out.println(entry.getKey() +":" + entry.getValue()) ;
    }

    System.out.println("******************遍历所有的value*******************");
    /*
     * 遍历value
     * Collection<V> values()
     * 
     */
    Collection<Integer> values = map.values();
    for (Integer value : values) {
        System.out.println(value);
    }


Map原理


HashMap是当今查询速度最快的数据结构,内部使用数组实现,但是HahsMap可以根据key的hashcode值计算
出其在数组的对应的下标,这省去了遍历数组查询的工作,从而查询性能不受数据量所影响.
但是会影响HashMap查询性能的情况是HashMap中长生链表而长生连接点的一个主要情况为:
当两个Key的hashcode值相同,但是equals比较不一样时,在HashMap中就会产生链表.
注:hashcode决定元素在数组中的位置,equals决定key是否重复.
为此,API手册对于这两个方法的重写时有明确说明的:
1:成对重写
对需要重写一个类的equals方法时就要连同重写hashCode方法.
2:一致性
当两个对象equals比较为true时,hashCode方法返回的数字必须相等,反之亦然(不是必须).
hashCode相等时,equals比较尽量保证为true.因为若使用这个类的实例作为key
在HashMap中使用时,若两个对象的hashCode值相等,但是equals比较不为true时会产生链表.
3:稳定性
当参与equals比较的属性值没有发生改变的前提下,多次调用hashCode方法返回的数字应当保持不变.

10-06 10:05