集合总结

ArrayList是基于数组实现的,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低。
ArrayList中允许元素为null,在查找给定元素索引值等的方法中,源码都将该元素的值分为null和不为null两种情况处理
ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。
Collections.synchronizedList实现线程安全的方式是建立了list的包装类,对部分操作加上了synchronized关键字以保证线程安全。但其iterator()操作还不是线程安全的。
CopyOnWriteArrayList在线程对其进行些操作的时候,会拷贝一个新的数组以存放新的字段,没有加任何同步关键字,每次写操作都会进行一次数组复制操作,然后对新复制的数组进行些操作,不可能存在在同时又读写操作在同一个数组上(不是同一个对象),而读操作并没有对数组修改,不会产生线程安全问题,在列表有更新时直接将原有的列表复制一份,并再新的列表上进行更新操作,完成后再将引用移到新的列表上。旧列表如果仍在使用中(比如遍历)则继续有效。如此一来就不会出现修改了正在使用的对象的情况(读和写分别发生在两个对象上),同时读操作也不必等待写操作的完成,免去了锁的使用加快了读取速度。
在线程数目增加时CopyOnWriteArrayList的写操作性能下降非常严重,而Collections.synchronizedList虽然有性能的降低,但下降并不明显。在多线程进行读时,Collections.synchronizedList和CopyOnWriteArrayList均有性能的降低,但是Collections.synchronizedList的性能降低更加显著。因此, CopyOnWriteArrayList,适用于以读为主。Collections.synchronizedList,适用于读写操作都比较均匀的地方。
每个ArrayList实例都有一个容量,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。 
ArrayList使用add(E e)时先调用了ensureCapacity(size+1)方法,之后将元素的索引赋给elementData[size],而后size自增。数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。
ArrayList有两个私有属性:elementData存储ArrayList内的元素,size表示它包含的元素的数量。
ArrayList提供了三种方式的构造器,可以构造一个默认初始容量为10的空列表、构造一个指定初始容量的空列表以及构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回它们的顺序排列的。
关于ArrayList和Vector区别如下:
ArrayList在内存不够时默认是扩展50% + 1个,Vector是默认扩展1倍。
Vector提供indexOf(obj, start)接口,ArrayList没有。
Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为线程安全需要更大的系统开销。

HashMap基于哈希表的 Map 接口的实现。允许使用 null 值和 null 键。不保证映射的顺序,特别是它不保证该顺序恒久不变。
HashMap的底层主要是基于数组和链表来实现的,它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储的位置。HashMap中主要是通过key的hashCode来计算hash值的,只要hashCode相同,计算出来的hash值就一样。如果存储的对象对多了,就有可能不同的对象所算出来的hash值是相同的,这就出现了hash冲突,HashMap底层是通过链表来解决hash冲突的。
我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到应保存在数组中的索引,计算方法是h&(length-1),这样实现了均匀的散列,而且效率要高很多,找到的bucket位置用来储存Entry对象
当hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中
当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,然后会调用keys.equals()方法遍历链表找到链表中正确的节点,最终找到要找的值对象。
如果HashMap的大小超过了数组大小*loadFactor负载因子(load factor)定义的容量,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置
HashMap不是线程安全的,首先如果多个线程同时使用put方法添加元素,而且假设正好存在两个put的key发生了碰撞(hash值一样),那么根据HashMap的实现,这两个key会添加到数组的同一个位置,这样最终就会发生其中一个线程的put的数据被覆盖。第二就是如果多个线程同时检测到元素个数超过数组大小*loadFactor,这样就会发生多个线程同时对Node数组进行扩容,会引起死循环。
如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap。
HashTable源码中是使用synchronized来保证线程安全的,当一个线程访问HashTable的同步方法时,其他线程如果也要访问同步方法,会被阻塞住,如当一个线程使用put方法时,另一个线程不但不可以使用put方法,连get方法都不可以,效率很低。
ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。

Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
Cookie对象使用key-value属性对的形式保存用户状态,一个Cookie对象保存一个属性对,一个request或者response同时使用多个Cookie
如果不设置 过期时间,则表示这个 Cookie生命周期为 浏览器会话期间 , 只要关闭浏览器,cookie就消失了.
一般保存在 内存,不在硬盘;如果设置了过期时间, 浏览器会把cookie保存在硬盘上,关闭再打开浏览器, 这些cookie 依然有效直到 超过的设置过期时间;

session 存储在Server段的内存进程中的,而这个进程相当不稳定,经常会重启,这样重启的话,就会造成Session失效,用户就必须要重新登录,用户体验相当差
当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照sessionid把这个session检索出来….使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,
Session ID的保存方法:[1] 使用Cookie来保存,服务器通过设置Cookie的方式将Session ID发送到浏览器。[2]URL地址重写是对客户端不支持Cookie的解决方案。URL地址重写的原理是将该用户Session的id信息重写到URL地址中。服务器能够解析重写后的URL获取Session的id。
[3] 第三种方式是在页面表单里面增加隐藏域,这种方式实际上和第二种方式一样,只不过前者通过GET方式发送数据,后者使用POST方式发送数据

cookie 和session 的区别:
1.cookie数据存放在客户的浏览器上,session数据放在服务器上.
       简单的说,当你登录一个网站的时候,如果web服务器端使用的是session,那么所有的数据都保存在服务器上面,
客户端每次请求服务器的时候会发送 当前会话的session_id,服务器根据当前session_id判断相应的用户数据标志,以确定用户是否登录,或具有某种权限。
2.cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session
3.session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用COOKIE
4.单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。Session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型
5.session很容易失效,用户体验很差;

java io
1、单纯的InputStream是一个抽象类,实际在使用时一般都是InputStream的子类,比如在socket里面的SocketInputStream。
2、空方法:available() 一直返回0,close()、mark(int readlimit)什么都没有操作,markSupported()总是返回false,reset()总是抛异常 throw new IOException(“mark/reset not supported”);,具体的操作由子类去实现。
3、重要:abstract int read(),是一个抽象的方法,具体的操作由子类去实现。
4、重要: read(byte b[])实际调用重载函数 int read(byte b[], int off, int len)。
5、最重要的当属于 int read(byte b[], int off, int len)方法了,实际调用的还是read()方法,具体看代码,简单,深入:

[1]面向流与面向缓冲:Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。

[2]阻塞与非阻塞IO:Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

[3]选择器:Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值