容器分为Collection集合类,和Map键值对类2种。
Collection 接口:
1. List 接口 (按插入顺序保存,元素可以重复):
2. set 接口(不能有重复元素)
Queue接口(一头进一头出):
Map接口:
所有Collection的构造器都可以接收另一个Collection(可以不同类型)来填充自己。
Collections类也有一些实用的static方法,其中包括fill,同Arrays一样只复制同一对象引用来填充整个容器的,并且只对List对象有用,但是所产生的列表可以传递给构造器或addAll方法。
使用最多的就是第三层的容器类,其实在第三层之上还有一层Abstract 抽象类,如果要实现自己的集合类,可以继承Abstract类,而不必实现接口中的所有方法。
数组有Arrays类填充,容器也有Collections类填充,这种工具类中一般都是静态方法不用创建它们的对象直接调用,所以很方便。.
Collections.addAll() 将一些元素添加到一个Collection中
Collections.nCopies() 复制一些元素到Collections中,返回一个List集合
Collections.fill() 复制元素填充到集合当中
享元模式:可在普通解决方案需要过多对象,或者产生普通对象太占用空间时使用享元。享元模式使得对象的一部分可以被具体化,因此,与对象中的所有事物都包含在对象内部不同,可以在更加高效的外部表中查找对象的一部分或整体。
AbstractList 实现了List接口
AbstractMap 实现了Map接口
AbstractSet 实现了Set接口
Collection中的方法在List和Set中都实现了,List还添加了额外的方法,如get(),这在Collection和Set中都没有,因为Set无序所以无法确定位置。
迭代器接口方法:
hasNext(); 判断是否存在下一个对象元素
next(); 获取下一个元素
remove(); 移除元素
可选的方法就是该方法在父类中会抛出异常,如果子类不需要该方法就不必重写它,一但调用则抛出异常,如果需要就去重写它的功能。
Collection中的 各种添加 移除方法都是可选的。AbstractList ,AbstractSet,AbstractQueue中就是实现了可选功能,调用这些抽象类中的方法就会抛出异常。
执行各种不同的添加和移除的方法在Collection接口中都是可选操作。这意味着实现类并不需要为这些方法提供功能定义。
可选操作声明调用某些方法将不会执行有意义的行为,相反,它们会抛出异常。如果一个操作是可选的,编译器仍旧会严格要求你只能调用该接口中的方法。
UnsupportedOperationException必须是一种罕见的事件。即,对于大多数类来说,所有的操作都应该是可以工作的,只有在特例中才会有未获得支持的操作。在Java容器类库中确实如此,因为只有99%的时间里面使用的所有的容器类,如ArrayList、LinkedList、HashSet和HashMap,以及其他具体的实现,都支持所有的操作。
如果一个操作是为获得支持的,那么在实现接口的时候就可能会导致UnsupportedOperationException异常。
最常见的未获支持的操作,源于背后由固定尺寸的数据结构支持的容器。当使用Arrays.asList将数组转换为List时,就会得到这样的容器。还可以通过使用Collections类中不可修改的方法,选择创建任何会抛出UnsupportedOperationException的容器:
因为Arrays.asList会生成一个List,它基于一个固定大小的数组,仅支持那些不会改变数组大小的操作。任何会引起对底层数据结构的尺寸进行修改的方法都会产生一个UnsupportedOperationException异常,以表示对未获支持操作的调用。
Arrays.asList产生固定尺寸的List,而Collections.unmodifiableList产生不可修改的列表。set方法可以在二者产生的对象之间有粒度上的变化。Arrays.asList可以修改元素,没有违反尺寸固定的特性。
ArrayList的Add方法实现原理: 如果elementData中元素数大于10个则复制一份旧数组并扩容再将元素添加就去 。
remove方法:使用本地方法直接操作内存改变,但这也属于浅复制多线程下可能复制出错导致删不掉元素, numMoved = size - index - 1; 最后将数组最后一个元素设为null 。
size是ArrayList内元素个数,并不是数组长度,ArrayList内数组长度默认最小是10。
LinkedList 内部是由一个内部静态类实现的一个双向链表。
List接口:
ListIterator迭代器接口方法:
Set接口:继承来自Collection接口的行为。
存储顺序不同的set实现不仅具有不同的行为,而且它们对于可以在特定的set中放置元素的类型也有不同的要求:
加入Set
的元素必须定义equals()
方法以确保对象的唯一性。
HashSet底层是由HashMap实现的,add进去的值就put在了map中作为key值,为了减小开销,所有value值为同一个new Object() 。 HashSet如果没有其他限制,应该默认选择,因为它对速度进行了优化。
必须为散列存储和树形储存都创建equals方法,但是hashCode只有在类被置于HashSet和LinkedHashSet时才必须。但都应该覆盖equals方法,总是同时覆盖hashCode方法。
保证不重复:先检测有没有相同的hash值,如果没有就添加元素,如果有在equal比较key值,相同则不允许添加,不同则允许添加入map。
如果一个对象被用于任何种类的排序容器中,例如SortedSet
(TreeSet
是其唯一实现),那么它必须实现Comparable
接口。
注意,SortedSet
的意思是“按对象的比较函数对元素排序”,而不是指“元素插入的次序”。插入顺序用LinkedHashSet
来保存。
SortedSet中的元素可以保证处于排序状态,SortedSet接口中的下列方法提供附加的功能:
LinkedHashSet也是由 LinkedHashMap实现。
TreeSet底层由TreeMap实现。TreeSet 实现了NavigableSet接口,而NavigableSet接口继承了SortedSet接口 ,NavigableSet提供了搜索功能方法,SortedSet 提供了排序功能方法,
TreeSet(Comparator super E> comparator) ,凡是带有比较排序功能容器都有一个能传入比较器Comparator对象的构造方法,使用这个构造器可以根据自己实现的Comparator排序。
除了并发应用外,Queue在Java SE5中仅有的两个实现是LinkiedList
和PriorityQueue
,它们仅有排序行为的差异,性能上没有差异。
Queue接口:
列表中的每个对象都包含一个字符串和一个主要的以及次要的优先级值,该列表的排序顺序也是通过实现Comparable而进行控制的。
双向队列就像一个队列,但是可以在任意一段添加或移除元素。在LinkedList中包含支持双向队列的方法,但在Java标准类库中没有任何显式的用于双向队列的接口。因此,LinkedList无法去实现这样的接口,无法像前面转型到Queue那样向上转型为Deque。但是可以使用组合创建一个Deque类,并直接从LinkedList中暴露相关方法:
Map接口:
get进行线性搜索时,执行速度会相当慢,这正是HashMap提高速度的地方。
散列表(哈希表)即 数组+单链表 默认数组大小是16,数组大小一直保持是2n, 数组下标是 key的hash值进行扰乱 再与 数组大小减1 按位 做 & 运算得出。
HashMap使用特殊值,称作散列码,来取代对键的缓慢搜索。散列码是相对唯一的,用以代表对象的int值,它是通过将该对象的某些信息进行转换而生成的。
hashCode()是跟类Object中的方法,因此所有Java对象都能产生散列码。HashMap就是使用对象的hashCode进行快速查询的,此方法能够显著提高性能。
使用SortedMap(TreeMap的唯一实现),可确保键处于排序状态。使得它具有额外的功能,这些功能由SortedMap接口中的下列方法提供:
为了提高速度,LinkedHashMap散列化所有的元素,但在遍历键值对时,却又以元素的插入顺序返回键值对。此外,可以在构造器中设定LinkedHashMap,使之使用基于访问的最近最少使用(LRU)算法,于是没有被访问过的元素就会出现在队列的前面。
键值对是以插入顺序进行遍历的,但是,在LRU版本中,在只访问过前六个元素后,最后三个元素移到了队列前面。然后再次访问元素0时,它被移到了队列后端。
映射表(也称为关联数组Associative Array)
HashMap使用了特殊的值,称作散列码(hash code),来取代对键的缓慢搜索。散列码是“相对唯一”的、用以代表对象的int值,它是通过将该对象的某些信息进行转换而生成的。
hashCode()是根类Object中的方法,因此所有对象都能产生散列码。
对Map中使用的键的要求与对Set中的元素的要求一样:
任何键都必须具有一个equals()方法;
如果键被用于散列Map,那么它必须还具有恰当的hashCode()方法;
如果键被用于TreeMap,那么它必须实现Comparable。