1. 集合框架体系概述
为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,为方便对
多个对象的操作,就对对象进行存储,集合就是存储对象最
常用的一种方法.
数组和集合类同时容器,有何不可?
数组虽然也可存储对象,但长度固定; 而集合长度可变
数组是存储同一类型基本数据类型,集合能存储任意对象
集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象.
图1: 集合框架图
图2: Java框架构成关系图
每个容器对数据的存储方式不同,这种存储方式称为数据结构.
2. 集合框架共性方法
//发现java中对方法的学习用增删查改不能很好的概括,增,删,改可以,但查可以分为判断和获取.这样就五种操作了.增,删,判,获(查),改.
//集合存储的都是对象的引用(地址)
boolean add(E e)添加指定的元素(可选操作)
boolean addAll(Collection<? extends E> c) 将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
clear(): 清空集合中所有元素
boolean contains(Object o) 是否包含指定元素
boolean containsAll(Collection<?> c) 只判断参数中的集合是否都包含在A集合内,最终A集合没有任何变化.
boolean isEmpty() 判断是否为空
boolean remove(Object o) 移除单个实例
boolean removeAll(Collection<?> c) 取差集
boolean retainAll(Collection<?> c) 取交集
int size():返回collection中的元素.
Object[] toArray() 这个可以理解
<T> T[] toArray(T[] a) 应这么写String[] y = c.toArray(new String[collection.size()])较好
重点讲讲用于查找的Iterator迭代器接口
Iterator it = al.iterator(); /*实际上是集合类在List和Set都包含的iterator方法,返回Iterator对象,具体实现方式是内部类.可以认为是继承了AbstractList,实现了Iterable接口.把取出方式定义成内部类,每个容器的数据结构不同,取出的动作细节也不一样.但是都用共性的判断和取出,可以将共性方法抽取.对外提供了Iterator方法.*/
迭代器使用
while(it.hasNext()){
System.out.println(it.next());
}
老外为了节省空间,写成这样
for(Iterator it = al.iterator();it.hasNext(); ){
System.out.println(it.next());
}
3. List序列共性方法(List也被成为序列, 它的对象的元素有序可重复,正因为有序,所以操作角标的方法都是该体系特有的方法)
增 void add(int index, E element) 在列表的指定位置插入指定元素(可选操作)。
删 E remove(int index) 移除列表中指定位置的元素(可选操作)。
查 ListIterator<E> listIterator() 返回此列表元素的列表迭代器(按适当顺序)。
改 E set(int index, E element) 用指定元素替换列表中指定位置的元素(可选操作)。
获取 E get(int index) 返回列表中指定位置的元素。
list因此多了一种取出所有元素的方法:
for(int i=0;i<al.size();i++){
输出al.get(i);
}
获取<E> subList(int fromIndex, int toIndex) 返回列表中指定的 fromIndex(包括)和 toIndex(不包括)之间的部分视图。
List有自己更强功能的的ListIterator是Iterator的子接口,是带下标的.
集合引用和迭代器引用在同时操作元素,通过集合获取到对应的迭代器后,在迭代中,进行集合引用的元素添加,迭代器并不知道,所以会出现ConcurrentModificationException异常情况。ListIterator列表迭代器接口具备了对元素的增、删、改、查的动作。
原 查 next() 但是 增加了previous()
原 删 void remove()
增加了特有了
增void add(E e)
改 void set(E e)
和独有的int nextIndex(), int previousIndex() 和 int nextIndex()
4. List集合的三个常见子类对象(List有序可重复,因为体系有索引)
ArrayList: 底层使用数组结构, 查询块,增删稍慢. 线程不同步,JDK1.2以上
LinkedList: 底层是链表结构, 增删块,查询稍慢,JDK1.2以上
Vector: 底层使用数组结构, 查询块,增删慢. 线程同步.被ArrayList替代了1.0时代产物. 枚举就是Vector特有的取出方式.
ArrayList详解:拥有角标的方法是其特有方法
可变长度数组的原理 :当元素超出数组长度,会产生一个新数组,将原数组的数据复制到新数组中,再将新的元素添加到新数组中。
ArrayList:是按照原数组的 50%延长构造一个初始容量为10的空列表。
Vector:是按照原数组的 100%延长
LinkedList详解:特有的add,get,remove方法
在1.6后新方法
boolean offerFirst(E e)
在此列表的开头插入指定的元素。
boolean offerLast(E e)
在此列表末尾插入指定的元素。
E peek()
获取但不移除此列表的头(第一个元素)。
E peekFirst()
获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
E peekLast()
获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
E pollFirst()
获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
E pollLast()
获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
Vector详解
枚举是Vector特有的取出方式hasMoreElements()和nextElement()方法,发现枚举和迭代器很像.其实枚举和迭代一样的.
但迭代取代了枚举,不仅是名字更短一点, 还包含可移除的操作, 使用范围也更广.
推荐使用迭代,而不是枚举.
1
2
3
4
5
6
7
8
9
10
11
|
//ArrayList练习一: 去除ArrayList中的重复元素 //思路:新建一个容器,如果新容器没有就存进去,最后返回即可. public static ArrayList singleElement(ArrayList al){ ArrayList newAl = new ArrayList(); for (Iterator it = al.iterator();it.hasNext();){ Object next = it.next(); if (!newAl.contains(next)) newAl.add(next); } return newAl; } |
//注意:使用iterator.next()取出时每次只取出一个,而不是取出多个.会造成NoSuchElementException
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//ArrayList练习二:将自定义对象作为元素存到ArrayList集合中,并取出重复元素. 比如:存人对象,同姓名同年龄视为同一个人,为重复元素. //思路:1.对人描述,封装成对象 2.定义容器,将人存入 3.取出重复元素 4.取出 //主要第三部分,如何去除重复元素 public static List singleElement(List list){ List newList = new ArrayList(); for (Iterator it = list.iterator();it.hasNext();){ Person p = it.next(); if (!newList.contains(p)) newList.add(p); } return newList; } //总结:本来是使用void,虽说引用是实际参数地址的拷贝,对拷贝的操作会牵扯到原来的数据,但是不能整个集合赋值给新的引用参数的地址,这是无效的,所以必须有返回值才行. |
总结: 在List下的ArrayList和LinkedList的contains和remove方法都是使用了了obj的equals方法.可以自己重写equals方法判断集合内两对象是否"一致".
5. Set集合的两个常见子类对象(Set集合的方法和Collection是一模一样,所以不用多讲)
元素是无序,不可重复的
HashSet(线程是不同步)
底层数据结构是哈希表,线程非同步.
通过hasHashCode()和equals()来完成
如果元素的HashCode相同,才会判断equals是否为true
如果元素的HashCode不同,不会调用equals,直接是不等.
注意,对于判断元素是否存在,以及删除等操作,依赖方法都是hashcode和equals方法. 在使用HashSet,一定要按需覆盖int hashCode()和boolean equals (Object obj)方法.按照优先级判断元素是否能添加删除.
TreeSet(可以对Set集合中的元素进行排序)
底层数据结构是二叉树
通过int compareTo()来完成增,删,查
TreeSet排序
第一种方式:实现Comparable接口,覆盖int compareTo()方法,让元素自身具备比较性
第二种方式:构造实现Compare接口,覆盖int compare(Object o1, Object o2)方法,将比较器对象作为参数传递给TreeSet集合的构造函数.
6. Map集合的三个常见子类对象
将键映射到值的对象,一对一对往里存,而且要保证键的唯一性.
添加
put(K key, V value)
putAll(Map<? extends K, ? extends V> m)
删除
clear()
remove(Object key)
判断
containsKey(Object key)
containsValue(Object value)
isEmpty()
获取
get(Object key)
size()
values()
entrySet()
keySet()
---HashTable: 底层是哈希表,不允许空键空值,线程同步,jdk1.0,效率低
---HashMap: 底层是哈希表,允许空键空值,线程不同步,jdk1.2,效率高
---TreeMap: 底层是二叉树,线程不同步,jdk1.2
Map和Set很像,其实Set底层就是使用了Map集合.
Map集合的共性方法注意
1.添加元素,如果出现相同的键,那么后添加的值会覆盖原有键对应的值, put方法会会返回被覆盖的值
2.可通过get方法的返回值来判断一个键是否存在,通过返回null判断.
3.获取map集合中所有的值
两个重要的获取方法: keySet()和entrySet()
1.通过keyset()获取key的Set集合,然后Iterator获取key,最终get(Object key) 获取.
2.通过entryset()获取关系,然后Iterator获取键值对,最终Map.Entry的getKey和getValue方法获取.
其实Map.Entry也是一个接口,它是Map接口中的一个内部接口
练习一:使用HashMap
每个学生都有归属地
学生Student,地址addr
学生属性:姓名和年龄,注意姓名和年龄相同视为同一个学生,需保证学生的唯一性
我的主要代码
练习二: 对学生对象的年龄进行升序排序 (使用TreeMap即可)
练习三: "sdfkgjlsk"获取该字符串中字母出现的次数
Map扩展: 无非就是集合套集合,用keyset()对于这种复杂的结构似乎简化了操作,但不管怎样还是利于理解的.比使用entryset要简便.