集合框架就是集合容器因为内部的数据结构不同,而不断向上抽取的系统化产物。
1 集合框架的结构
1.1 Collection接口
Collection接口是框架的顶层接口。因为Collection接口的子类并非全部都有角标(只有List子接口有,Set子接口没有),所以只有增删查三大方法,没有改(如果要改只能删除再添加,但是不能保证序列一致,或者使用有角标的List接口),但有判断。
1.1.1 增——添加
boolean add(E e):一次添加一个元素。并返回一个
boolean addAll(Collection):将一个容器中的元素添加到当前容器中。
public class Day16 {
public static void main(String[] args){
Collection c = new ArrayList();
System.out.println(c.add(2313));
System.out.println(c);
System.out.println(c.add(2313));
System.out.println(c);
System.out.println(c.add(null));
System.out.println(c);
System.out.println(c.add("abcd"));
System.out.println(c);
System.out.println(c.add("efgh"+'a'+true));
System.out.println(c);
}
}
true
[2313]
true
[2313, 2313]
true
[2313, 2313, null]
true
[2313, 2313, null, abcd]
true
[2313, 2313, null, abcd, efghatrue]
PS:Collection的子类ArrayList可以存放不同的数据类型(StringBuffer也可以),但不能存放基本数据类型(StringBuffer可以),上程序之所以能放2313 'a'等,是因为jdk1.5后有自动装箱拆箱及机制。在数据被腐乳ArrayList之前自动把他们封装成基本数据类型封装类了。
1.1.2 删——删除
boolean remove(Object obj);删除一个指定对象。如果没有此对象就返回false
boolean removeAll(Collection coll);删除参数容器和本容器中相同的元素。
void clear();直接将容器中的元素清空。
1.1.3 判断
boolean contains(Object obj);是否包含指定元素。
boolean containsAll(Collection coll);是否包含指定容器中的元素。容器中元素发生变化为true
boolean isEmpty();判断集合中是否有元素
public class Day16 {
public static void main(String[] args){
Collection c = new ArrayList();
c.add(2313);
c.add(2313);
c.add(null);
c.add("abcd");
c.add("efgh"+'a'+true);
int i = 5;
c.add(i);
c.remove("haha");
System.out.println(c);
System.out.println(c.contains("haha"));
System.out.println(c.containsAll(c));
System.out.println(c.isEmpty());
System.out.println(c.size());
}
}
[2313, 2313, null, abcd, efghatrue, 5]
false
true
false
6
1.1.4 查——获取
int size();获取该集合的长度。
Interator iterator();取出元素的手段:迭代器。
boolean retainAll(Collection coll);保留两集合的交集元素并输出判断(有或没有交集)
Object toArray();将集合转成数组。
1.2 迭代器
Iterator iterator();Iterator跟Collection一样,是一个接口。
因为每一个容器的数据结构都不同,所以该迭代器对象是在容器中进行内部实现的,也就是iterator方法在每个容器中的实现方式是不同的。java为了方便我们使用,统一封装成iterator给用户调用。
Iterator接口就是对所有的Collection容器进行元素取出的公共接口。
使用方法:作为集合对象的方法调用。Collection xxx = new ArrayList(); Iterator i = xxx.iterator();得到的不是Iterator实例,而是Iterator的运用于ArrayList的子类,这里运用了多态。
public class Day16{
public static void main(String[] args){
Collection coll = new ArrayList();
coll.add("abc1");
coll.add("abc2");
coll.add("abc3");
coll.add("abc4");
// System.out.println(coll.next);
System.out.println(coll);
//使用iterator调用迭代器,
<span style="white-space:pre"> </span>//用while循环
Iterator i = coll.iterator();
while(i.hasNext()){
System.out.println(i.next());//next是迭代器的方法
}
//用for循环
for(Iterator i1 = coll.iterator();i1.hasNext();){
System.out.println(i1.next());
}
}
PS: 注意用while方法的话在循环完毕后迭代器iterator仍会存在。如果用for循环则可以在循环完毕后释放iterator,更干净。
注意使用迭代器iterator过程中,最好不要对集合进行操作,因为迭代器是事先读取集合长度后操作的,是属于迭代器的动作,如果同时进行了增删这个集合操作,则迭代器不知道读不读,应该读到哪里,即引发了并发操作异常。
所以在迭代器里,只能用迭代器的方法操作数据。原本的iterator迭代器只提供了move操作,即从集合中去除该元素。
如果我们需要对迭代中做更多的增删改查操作,就可以运用List类特有的迭代器ListIterator。
它具有add() set() 等方法。同时还有向前迭代的功能hasPrevious,previous
注意迭代器在循环中迭代完一次之后,不会回复首位的,它的状态已经被记录,如果再用它去迭代一次,只能一致输出null。
public class Day16{
public static void main(String[] args){
List l = new ArrayList();
l.add("abc1");
l.add("abc2");
l.add("abc3");
System.out.println("list="+l);
Iterator i = l.iterator();
while(i.hasNext()){
// i.next() = "abc";
Object o = i.next();
System.out.println(o);
// System.out.println(i.next());
}
}
}
Collection的两个重要接口(仍然是一个接口)
——List:有序(存入和取出的顺序一致),元素都有索引(或角标),允许重复元素
——Set:元素不能重复,无序。(注意这里的无序不是说它的数据随机存放,而是不按照添加顺序存放——因为他们有自己的排序方式。HashSet是按HashCode和内容,TreeSet是按比较器comparator或让类本身具备可比性comparable)
2.1 List
除了Collection的方法,其特有的常见方法,基本是操作角标的方法(因为List有角标):
2.1.1 添加
void add(int index,E element):将元素插入指定角标位置。
boolean addAll(int index,collection c):将参数容器中的元素插入指定位置角标。
2.1.2 删除
Object remove(index);删除指定角标的元素
2.1.3 修改(因为有List有角标,所以允许修改操作)
Object set(index,element);指定角标修改元素
2.1.4 获取
Object get(index);获取特定角标的元素;
int indexOf(object);根据元素获取元素第一次的角标。如果没有元素,返回-1.
int lastIndexOf(object);根据元素获取元素最后出现的角标。
List subList(int from, int to);获取列表中的一部分,包含fromIndex,不包含toIndex。
PS:正因为List接口有角标,它可以按照角标来操作,比如按角标获取元素。所以List接口的子类对象除了按照Collection的方法使用迭代器iterator之外,还可以像传统的数组一样获用角标获取。
public class Day16{
public static void main(String[] args){
List l = new ArrayList();
for (int x=0;x<10;x++){
l.add("haha_"+(x+10));
}
System.out.println(l);
Iterator i = l.iterator();
while(i.hasNext()){
System.out.print(i.next());
}
System.out.println();
System.out.println("\r\n"+"-------------");
for(Iterator i1=l.iterator();i1.hasNext();){
System.out.print(i1.next());
}
System.out.println();
System.out.println("\r\n"+"-------------");
for(int x=0;x<l.size();x++){//角标取值,List接口特有的方法,因为List有角标
System.out.print(l.get(x));
}
}
}
3 List下面的主要类
List:
——Vector:内部是数组结构,是同步的。增删,查询都很慢
——ArrayList:内部是数组结构,是不同步的,替代了Vector。查询速度快,增删速度慢。
——LinkedList:内部是链表结构,是不同步的,增删的速度很快,查询速度慢。
3.1 LinkedList特有的主要方法
因为LinkedList是链表结构,只有索引,没有角标,所以它没有按照角标操作的方法。
增加
addFirst();将指定元素放入LinkList开头,
addLast();将指定元素放入LInkList结尾。
在jdk1.6后,他们分别被offerFirst(); offerLast();代替,没有区别。
获取
getFirst();按索引顺序获取,但不移除,如果链表为空,抛出NoSuchElementException
getLast();按倒叙的索引顺序获取,但不移除
jdk1.6后更新方法peekFirst();替代getFirst(),peekLast();替代getLast();获取但不移除,如果链表为空,返回null
removeFirst();获取并移除,如果链表为空,抛出NoSuchElementException
removeLast();
jdk1.6后更新方法为pollFirst();替代removeFirst();获取并移除,如果链表为空,返回null。pollLast();同理。
package demo;
import java.util.*;
public class Day17 {
public static void main(String[] args){
LinkedList lk = new LinkedList();
lk.add("haha1");
lk.addFirst("haha2");
lk.addLast("haha3");
System.out.println(lk);
Iterator i = lk.iterator();
while(i.hasNext()){
System.out.println(i.next());
}
System.out.println("-----------");
for(Iterator i1 =lk.iterator();i1.hasNext();)
System.out.println(i1.next());
System.out.println("-----------");
Iterator i2 = lk.iterator();
String[] s=new String[lk.size()];
int x = 0;
while(i2.hasNext()){
s[x]=(String)i2.next();
x++;
}
for(String str:s){
System.out.println(str);
}
System.out.println("-----------");
while(lk.peek()!=null){
// while(true){
System.out.println(lk.poll());
}
// System.out.println(lk);
// while(true){
// System.out.println();
// }
}
}
注意第二次用lk的迭代器必须要再创建一个Iterator i2,否则会全部输出null
4 Set
4.1 Set是跟List区分开的一个接口,其元素不可以重复,而且无序。
——HashSet:内部哈希表结构,是不同步的。
——TreeSet:可以对Set集合中的元素进行排序,是不同步的。
public class Day17{
public static void main(String[] args){
Set s = new HashSet();
s.add("haha1");
s.add("haha2");
s.add("haha3");
s.add("haha4");
Iterator i = s.iterator();
while (i.hasNext())
System.out.println(i.next());
}
}
haha4
haha3
haha2
haha1
并非按照输入顺序输出,而是按照Hash地址输出。
4.2 HashSet用哈希表确定元素是否相同。
①先判断两个元素的哈希值是否相同。用HashCode()方法得到元素的哈希值。
②如果相同,再判断两个对象的内容是否相同(因为有可能两个元素被人为设定为同一个哈希值)。用的是equals方法。
如果HashCode不同,就不再判断内容equals
/*在HashSet集合中存储Person对象,如果姓名和年龄相同,视为同一人,是为相同元素
*
*/
class Person{
private String name;
private int age;
Person(String name,int age){
this.name=name;
this.age=age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public int hashCode(){
// System.out.println(name.hashCode());
return name.hashCode()+age*11;
}
public boolean equals(Object obj){
if(!(obj instanceof Person))
throw new ClassCastException("类型错误");
Person p = (Person)obj;
return this.name.equals(p.name)&&this.age==p.age;
}
}
class Day17{
public static void main(String[] args){
HashSet hs = new HashSet();
hs.add(new Person("zhangsan",10));
hs.add(new Person("lisi",14));
hs.add(new Person("wangwu",12));
hs.add(new Person("zhangsan",10));
System.out.println(hs);
Iterator i = hs.iterator();
while(i.hasNext()){
Person obj = (Person)i.next();
System.out.println(obj.getName()+"..."+obj.getAge());
}
}
}
[demo.Person@aa9c30e2, demo.Person@d09b2ca5, demo.Person@32b12d]
zhangsan...10
wangwu...12
lisi...14
PS:HashSet判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals。
4.3 LinkedHashSet
LinkedHashSet可以使其无序变有序,但仍然不可重复。
public class Day17{
public static void main(String[] args){
LinkedHashSet lh = new LinkedHashSet();
lh.add("zhangsan");
lh.add("lisi");
lh.add("wangwu");
lh.add(10);
lh.add("wangwu");
System.out.println(lh);
}
}
[zhangsan, lisi, wangwu, 10]
4.4 TreeSet
TreeSet底层是二叉树结构。可以给set集合中的元素进行指定顺序排序,默认情况下是通过元素自然顺序排序的。
保证元素元素唯一性的依据是看compareTo方法返回结果是否为0,是0就视为相同,不存。(而HashSet是根据hashCode和equals的判断来保证为一)
如果不要按照对象中具备的自然顺序进行排序。或者对象不具备自然排序,就让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法,
然后把其对象传入集合作为集合使用的比较器。
blic class Day17{
public static void main(String[] args){
TreeSet<String> ts = new TreeSet(new LengthCom());
ts.add("hahaaaa");
ts.add("heheaa");
ts.add("xixia");
ts.add("wuwuaaaa");
System.out.println(ts);
}
}
class LengthCom implements Comparator{
public int compare(Object o1,Object o2){
String s1 = (String)o1;
String s2 = (String)o2;
return(s1.length()-s2.length());
}
}
这是按照字符串长度来让TreeSet排序
PS:如果类本身实现了Comparable接口,覆盖了comparaTo(Object o)方法,即自身具备了比较性,而同时TreeSet的构造函数中也传入了比较器Comparator(覆盖了compare(Object o1,Object o2)方法),那么将以比较器的比较规则为准。
4.5 Vector
Vertor的一个特殊方法:
Enumeration elements();Enumeration是一个接口,它生产一系列元素,一次生成一个。该方法类似调用迭代器,为早期使用的方法。
Boolean hasMoreElements();判断是否有下一个元素。
nextElements();获取下一个元素。