集合类的由来:
对象用于封装特有数据,对象多了需要存储,如果对象的长度不确定,就使用集合存储。
集合特点
1、用于存储对象的容器。
2、集合的长度可变。
3、集合中不可以存储基本类型
集合容器因为内部的数据结构不同,有多种具体容器。
不断的向上抽取就形成了集合框架
框架的顶层就是:Collection接口。
顶层抽出的共有方法:
1、添加
2、删除
3、判断
4、获取
Iterator iterator();获取元素的方式 :迭代器
迭代器的使用:
使用for循环节省内存 循环结束后it不会驻留在内存中
如果用While 迭代器会驻留在内存中
for(Iterator it = coll.iterator();it.hasNext();){
System.out.println(it.next());
}
迭代器原理:
该对象必须依赖于具体容器,因为每一个容器的数据结构都不相同。
所以该迭代器对象是在容器中进行内部实现的。
对于使用容器者而言,具体的实现并不重要,只要通过容器获取到该实现的迭代器的对象即可。
也就是iterator方法。
Iterator接口就是对所有的Collection容器进行元素取出的公共接口
MyEclipse快捷键:查看接口的实现类:Ctrl+T
查看类或方法的引用 Ctrl+Shift+G
查看类的声明:F3
5、其它
retainAll(Collection coll) 获取两个集合中的交集
注意:retainAll与removeAll,removeAll删除集合中含有的所有对象.
eg:c1={1,2,3} c2={2,3,4} 吧c2放入c1中 removeAll后得到{1}
retainAll后{2,3}
Collection 下面的两个重要的子接口:
|--List:有序(存入和读出的顺序一致),元素都有索引,元素可以重复
|--Set:元素不能重复。(与List最大不同) 无序(也有可能出现有序的)
List:常见方法-->Collection有的List都有
特有方法:
修改:Object set(index,element);
获取:Object get(index);
int indexOf(Object o);
int lastIndexOf(Object o);
List<E> subList(int fromIndex, int toIndex);(包头不包尾)
总结:List可以对元素进行增删改查
注意:进行集合操作时候不进行迭代操作,进行迭代操作时候不进行集合操作
否则会出现对该对象进行了并发修改,但是不允许修改时候会报这种错误
改进方法-->迭代的时候不要使用iterator();方法。要使用listIterator();方法进行迭代 。
此方法为Iterator接口下面的子接口的方法的实现。
List接口下面的实现类:
|--Vector:(内部数据结构为数组数据结构,是同步的,意味着线程安全,但是效率低,现在已经基本上不用)注:最早的单列集合
Vector 类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。
但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。
增删查都很慢
遍历Vector的方法有三种(较ArrayList和LinkedList多出一种)
枚举遍历:
Enumeration en = Vector.elments();
Enumeration 有两个方法 hasMoreElement();
nextElement()
while(vector.hasMoreElements()){
System.out.println(vector.nextElement);
}
关于Enumeration:此接口的功能与 Iterator 接口的功能是重复的。此外,Iterator 接口添加了一个可选的移除操作,并使用较短的方法名。
新的实现应该优先考虑使用 Iterator 接口而不是 Enumeration 接口。
|--ArrayList(内部数据结构为数组数据结构,是不同步的。效率高。所以替代了Vector。如果是多线程 给ArrayList加锁)
可变长度数组实现原理,ArrayList会提前生成一个固定长度数组,如果大小不够大,新生成一个数组增加原来数组的一半。
读、查询速度快
|--LinkedList(内部数据结构为链表,非同步,效率高)
增删元素速度快
特有方法:addFrist(); addLast();
理解:因为LinkedList内部数据结构为链表,所以中间都是一环扣一环,但是头和尾部不是,所以很好添加新元素。
getFirst();获取但不删除 removeFirst();获取但是删除
重点:用LinkedList模拟堆栈或者队列的数据结构
堆栈:
栈特点:先进后出
队列:
特点:先进后出
实现:描述一个容器,给使用者提供一个容器对象完成这两种结构中的一种。
class MyQuere{
private LinkedList link;
MyQuere{
link = new LinkedList();
}
public void myAdd(Object obj){
link.addLast(obj);
}
public Object myGet(){
return link.removeFirst();
}
public boolean isNull(){
return link.isEmpty();
}
}
class MyStack{
private LinkedList link;
MyQuere{
link = new LinkedList();
}
public void myAdd(Object obj){
link.addLast(obj);
}
public Object myGet(){
return link.removeLast();
}
public boolean isNull(){
return link.isEmpty();
}
}
LinkdList---->JDK1.6新特性与1.5对比
addFirst();addLast();-->无返回
offerFirst();offerLast(); -->在此列表插入指定的元素。返回:true(根据 Deque.offerFirst(E) 的规定)(1.6新特性)
getFirst();getLast(); -->NoSuchElementException - 如果此列表为空.
peekFirst();peekLast(); -->此列表的第一个元素;如果此列表为空,则返回 null.(1.6新特性)
removeFirst();removeLast(); -->无返回
pollFirst();pollLast(); --> 获取并移除此一个元素;如果此列表为空,则返回 null。(1.6新特性)
总结:以上方法都来自于接口Quere,1.6新特性中的方法有特殊返回值。特殊返回值可以用来做判断标识。
补充:什么时候自动装箱?
基本类型值赋值给了对象引用的时候会自动装箱。
Set:一个不包含重复元素的 collection。
更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。
正如其名称所暗示的,此接口模仿了数学上的 set 抽象。无序的。
Set接口中的方法与Collection中的方法一致。
注意:Set去除只有一种方式:迭代器Iterator
|--HashSet 保证唯一的特点。该容器中不会有重复的对象。
此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序.
内部结构数据为哈希表,是不同步的。
补充:哈希表-->实质还是数组
一般数组的存入都是以脚标来存入,查找的时候顺序对比,产生结果。
而哈希表在存入的时候会按照存入的对象的特性,存入指定的位置(无序的原因)
function(Element){
一个算法,这个算法,对元素进行运算,并获取其位置。
注意:此算法就是hashcode();这个方法是由底层的windows做的,看不到。
我们可以做自己的算法:(简易原理实现:以字符串为例)另数组长度为10 即:0-9;
存放"ab"根据ab的asc值97+98=195
让195%10=5 ---->找到下标为5的位置进行比较,有就是有,没有就是没有。
---->插入的时候,当有5存在的时候就不会插入(这就是为什么Set不会有对象重复的原因)
---->注意:并不能说明地址(下标)相同就是相同的内容(例如:"ba")
---->所以当地址(下标)相同的时候,再要比较内容。
return index;
}
当要获取的时候再用一次该方法获取其位置,直接找到该元素。
总结哈希表:
哈希表确定元素是否相同
1.判断两个元素的哈希值是否相同。如果相同,进而比较两个对象的内容是否相同。
2.判断哈希值是否相同,其实判断的是对心的hashCode();的方法。
判断内容是否相同用的是equals();方法。
注意:如果哈希值不同,则不需要判断内容是否相同。
如果哈希值相同,但是内容不同(这种情况是很少的,因为哈希算法非常复杂,叫做哈希冲突),解决办法很多,可以顺延。
自定义对象的存储:(重点)
HashSet集合数据结构是哈希表,所以使用hashCode();方法来确定位置;如果位置相同,用equals();方法来确认内容是否相同。
如果不重写hashCode()方法,那么会调用父类(Object)的hashCode()方法。
如果不重写equals()方法,那么也会调用父类(Object)的equals()方法。父类的equals()方法比较的是地址,而没创建一个对象
肯定有一个新地址,所以equals()肯定不会相同,所以即便内容会相同,也会储存。
ArrayList和HashSet的区别:
ArrayList判断对象依赖的是equals();方法。
而HashSet判断对象用到的是hashCode();方法和equals();方法
所以当用ArrayList的remove()方法的时候,虽然对象的地址(下标)不同,但是只要内容相同,都会移除。
而HashSet就要用到hashCode()和equals()两个方法。
当我们想要保证对象唯一,并且还想要有序。在Set下面提供了一个实现类-->LinkedHashSet<E>
定义:
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。
此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。
此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。
注意,插入顺序不 受在 set 中重新插入的 元素的影响。(如果在 s.contains(e) 返回 true 后立即调用 s.add(e),则元素 e 会被重新插入到 set s 中。)
理解:就是HashSet与链表相结合。
|--TreeSet
使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
即:要想对自定义对象排序,必须将该类实现Comparator类,并实现其方法:compareTo()。
int compareTo(Object o){
比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
1、比较数值的时候,直接用逻辑符号>、<、=来判断。
2、比较字符串的时候,可以用public int compareTo(String anotherString)方法。
技巧:做差。
}
重要:判断元素唯一行的方法,就是根据比较方法的返回结果是否是0,如果是0,就是相同元素,不存。
总结:TreeSet对元素进行排序的方式之一:
让元素自身具备比较功能,元素需要实现Comparator接口,并且覆盖compareTo()方法。
如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序怎么办?
可以使用TreeSet的第二种排序方式:
让集合自身具备排序功能。集合创建时就要有。即:构造方法。
创建一个比较器,实现Comparator接口。实现compare()方法:两两元素进行比较。
两种方式的区别:第一种是利用元素自身的比较方法比,只需要传入一个元素即可。
第二种是利用集合自身的比较,要传入两个元素。
两个都存在的情况下面以构造器为主。实际开发用第二种比较多。
底层数据结构:
经典二叉树:当保存的时候使用二叉树保存,大的在右边,小的在左部。
int compare(Object o1,Object o2){ 比较用来排序的两个参数。
根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。
如果指定返回一个正整数,则意味着存入的时候都往二叉树的右侧存,即有了一定的顺序:插入的顺序是怎么样的,查询的顺序就是怎么样的。
}
注意:只要不是按照自然排序就利用比较器
==================================================================================================================================
Map:将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
与Collection区别:Map一次添加一对元素,Collection一次添加一个元素;Map也成为双列集合,Collection也成为单列集合。
根据定义:Map必须保证键的唯一性。
常用方法:
1、添加
V put(K key, V value):返回前一个key关联的值,如果没有返回null。
理解:如果该key之前没有对应的值,那么返回null,并且成功存入。如果key之前有值,那么返回之前的值(即把原来的值删除),并存入当前的值。
2、删除
void clear() 清空map集合
V remove(Object key):根据key值删除键值对
3、判断
boolean containsKey(Object key):有键吗?
boolean containsValue(Object value) :有值吗?
boolean isEmpty() :存在吗?
4、获取
V get(Object key) :通过键获取值。如果没有该键返回null。
int size() :返回键值对个数。
5、遍历
第一种:
Set<K> keySet() :得到所有键。-->既然是Set集合,那么就可以用迭代器迭代,为什么不反悔List?因为List不唯一。
第二种:
Set<Map.Entry<K,V>>entrySet(): 返回此映射中包含的映射关系的 Set 视图。(entry :登记)
原理:将键值对两个对象,封装成一个对象。
补充:内部接口
Map接口下面有一个内部接口Entry
interface Map{
interface Entry{
public void get();
}
}
为什么要这么做?因为Entry是将键值对封装成一个对象,如果要用Entry那么前提肯定是Map,外部规则中有内部规则,内部规则访问外部规则的内容。
所以要写成内部接口的形式。
Collection<V> values() :为什么返回Collection?因为键虽然唯一,但是值不唯一。
常用子类:
|--Hashtable:内部数据结构哈希表,是同步的。表注:最早的双列集合 重点:不允许null作为键,不允许null作为值。
Properties:Properties 类表示了一个持久的属性集。用来存储键值对形式的配置文件。可以和I/O技术相结合才能发挥最大的水平。
|--HashMap:内部结构是哈希表,不是同步的。允许null作为键,允许null作为值。(与Hashtable的主要区别)
LinkedHashMap<K,V>:
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。
此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。
此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。
注意,如果在映射中重新插入 键,则插入顺序不受影响。
|--TreeMap:内部结构是二叉树,不同步,可以对Map集合中的键进行排序
注意:HashSet此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。
Map集合练习:
获取字符串中每个字符出现的次数。
Utilities:
工具类:便捷开发。
|--Collections
此类完全由在 collection 上进行操作或返回 collection 的静态方法组成。
扩展:自定义排序方法
public static <T extends Comparable<? super T>> void MySort(List<T> list){
说明:<T extends Comparable<? super T>> 加限制,因为要保证调用该方法的集合可以比较。
}
补充:
Comparable接口与Comparator接口的区别:
Comparable 是排序接口。
若一个类实现了Comparable接口,就意味着“该类支持排序”。
即然实现Comparable接口的类支持排序,假设现在存在“实现Comparable接口的类的对象的List列表(或数组)”,则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序。
Comparator 是比较器接口。
我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);
那么,我们可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。
Comparable 是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序。
所以,如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法。
Comparator 是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。
|--Arrays
此类包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂。
除非特别注明,否则如果指定数组引用为 null,则此类中的方法都会抛出 NullPointerException。
注意:
数组转集合
static <T> List<T>asList(T... a) :返回一个受指定数组支持的固定大小的列表。
此方法同 Collection.toArray() 一起,充当了基于数组的 API 与基于 collection 的 API 之间的桥梁。
返回的列表是可序列化的,并且实现了 RandomAccess。
注意:转换之后的List集合不能进行增删操作,因为数组为固定长度。
如果数组中的元素是对象,那么转成集合时,直接将 数组中的元素 作为集合中的元素进行存储。
如果数组中的元素是基本数据类型,那么在转成集合时,会将 数组 作为集合中元素进行存储。
因为集合中不能存储基本数据类型,所以在转型的时候,List的泛型要注意:之前数组怎么定义的,泛型就怎么写。
集合转数组
Collection接口中的Object[]toArray()和<T> T[] toArray(T[] a)方法。
集合转成数组,可以对集合中的元素进行限定,不允许增删。
补充:给非同步的集合加锁。
class MyCollections{
public List synList(List list){
return new MyList(list);
}
class MyList implements List{
private List list;
private static final Object lock = new Object();
MyList(List list){
this.list = list;
}
public boolean add(Object obj){
synchronized(lock){
return list.add(obj);
}
}
public boolean remove(Object obj){
synchronized(lock){
return list.remove(obj);
}
}
...//实现其他方法并且都加锁。
}
}