---------------------- 黑马程序员 Android培训、期待与您交流! ---------------------
Collection集合类
为什么出现集合类?
面向对象的语言对于事物的体现都是以对象的形式。为了方便对多个对象进行操作,需要将对象进行存储,方式有二:数组和集合,这两者本质上都是容器。数据多了用对象存。
集合的特点:
1. 长度可变。2. 只能储存对象。3. 可以储存不同类型的对象。
集合就是容器,集合本身也是对象,也可以装填对象。集合有很多种,利用共性不断向上抽取,最终形成体系,称为“集合框架”。查阅顶层函数,创建底层对象。先看顶层,对整个体系有个大致的了解。顶层的是Collection接口,意为,集合、容器。
集合框架属于工具包的成员,在java.util中,顶层的Collection是一个接口。
Collection(接口)
add(Object obj):1. 参数类型是Object,可以接受任意类型对象;2. 集合中存放的都是对象的地址值(引用)。add语句的返回值是布尔型,表示是否已经存入。
可以直接打印集合,所有元素放在中括号中。
remove(Object obj);删除指定元素。返回布尔型值,表示是否已经删除。底层调用equals方法,确定删除哪个元素。
clear();清空集合。
retain(Collection e);集合之间取交集。例如,arr1.retainAll(arr2);,arr1中只会保留和arr2相同的元素。没有交集,显示为空。
removeAll();去掉相同元素保留不同元素。例如,arr1.removeAll(arr2);
contains();是否包含某元素。通过判断对象是否相同确定是否包含,实质是底层自动调用equals方法。通过复写equals方法来满足其他需求,例如比较对象的属性。
获取元素的目的是操作元素,不一定打印。
Iterator接口
Iterator是一个接口,可以建立引用,指向子类的对象;这个对象不是通过new建立的,而是通过iterator方法建立的,例如,Iterator it = arr1.iterator();。获取迭代器,用于取出集合中的元素。
1. 每个集合都需要取出数据进行操作,所以将取出动作定义在集合的内部,使得取出动作可以直接访问集合内部的元素。
2. 因为取出动作无法用一个功能就能描述,需要多个功能配合使用才行,例如,判断和取出。集合就将取出动作封装成对象,通过内部类(私有,隐式,Itr类)对取出动作进行描述。(这就是“内部类的设计”)通过iterator()方法创建对象,每个集合的iterator()内容都不一样。
也就是说,定义一个内部类,对集合的内部数据进行操作。每个集合都有内部类,以后再创建集合也需要内部类。
3.因为不同集合数据结构不同,所以取出动作细节不同。但都有共同的内容,就是所有的内部类都有判断和取出这2个功能。可以将这2个功能抽象出来,形成一个接口,或者说规则,即Iterretor,内部有判断和取出的函数声明但没有实质内容。这样就统一了取出方式,以后有集合,只需要实现Iterator接口即可。
迭代器出现的原因:
1.集合都有操作数据的需求。2. 取出数据的动作无法用一个函数描述,需要将取出封装成对象并通过内部类描述。3. 因为集合的数据结构不同,具体实现细节也不同,导致每个集合都有自己的取出对象和内部类。
如何获取集合的取出对象?每个集合都通过对外提供的一个方法,iteretor()。接口提供引用,集合提供对象,配合在一起使用,降低了使用者和取出动作的耦合性。2种方法:
Iterator it = arr1.iterator();//阅读性比较好。
while(it.hasNext())
{
sop(it.next());//注意:it.next()返回的是Object类指向。
}
-------------------------------------------
for(Iterator it = arr1.iterator();it.hasNext(); )//将it作为局部变量,节省了资源。
{
sop(it.next());
}
Collection(所有子类共享的方法add、remove、contains、clear、iterators)
List(类)元素是有序的,并且可以重复,因为该集合体系有索引。
ArrayList(类):底层的数据结构是数组结构。特点:查询速度块,但是增删稍慢。线程不同步。通过加锁可以用于多线程。可变长度数据,默认长度为10,超过10后,创建50%延长的新数据(节省空间),并将数据存到新数组中,依次执行。
LinkedList:底层使用链表数据结构,通过关联将数据连接在一起,每个数据只知道自己前面的数据。查询比较麻烦,但是增删很方便。
Vector:底层是数据数据结构,和ArrayList功能一样。元老,早于Collection存在。线程同步的,极少使用,被ArrayList替代了。100%延长数组。
Set(接口)元素是无序的(存入和取出的顺序不一定一致),不可以重复,因为没有索引。功能和Collection几乎是一致的。
HashSet底层数据结构是哈希表。
TreeSet底层是二叉树结构。
List集合
特有方法:凡是可以操作角标的方法都是该体系特有的方法。
判断元素是否相同,依据的是元素的equals方法。其他集合不同。
添加:
add(int index, Object element);只能有序添加,不能越位添加元素,否则异常。插入元素,后面的元素向后顺延。
add(int index, Collection)
删除:
remove(int index)
修改:
set(int index, Object element)
查找:
get(int index)
List subList(int from, int to);注意返回值类型为List,可以直接打印。
listIterator();列表迭代器,
indexOf(Object obj);
并发访问:不能对同一组元素做多种同时操作,会有安全隐患。例如,
Object obj = it.next();//迭代操作
if(obj.equals("java02"))
arr.add("C++");//迭代器不知道又添加了新元素。
安全的做法是,要么全用集合方法,要么全用迭代器方法,不能迭代中用集合。
Object obj = it.next();
if(obj.equals("java02"))
it.remove();
sop("obj="+obj);//集合中元素已经消失,但其指向仍然被Object使用,所以仍然可以打印”java02”。
List集合特有迭代器,ListIterater是Iterator的子类接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素,会发生并发异常。所以在迭代时,只能用迭代器的方法操作元素,Iterator的方法是有限的,只有判断、取出和删除的操作,适合遍历使用;如果想要其他的操作,就需要使用ListIterator接口。该接口只能通过List集合的 listIterator方法获取。例如,ListIterator it = arr.listIterator();和上面一样,遍历时的添加不能打印。
迭代器中的指针有记录功能,会记住所在位置。所以循环中,hasNext()判断一次,next()就执行一次,最为安全。
hasPrevious();指针前面是否有元素;hasNext();指针下面是否有元素。对应的获取元素方法:next();获取下一个元素(正向遍历);previous();获取上一个元素(逆向遍历)。
Vector集合与枚举
枚举:element()方法和Enumeration接口。早期,枚举是Vector特有的取出方式。其实枚举和迭代器是一样的。因为枚举的名称和方法的名称都过长,逐渐被迭代器取代了。
Vector集合取出元素的方式:1. 枚举。2. 迭代器。3. 遍历,for循环。4. 按角标索引,get方法。
LinkedList集合
特有方法:
addFirst();向后挤压,将先前的元素挤到后面。
addLast();向前挤压。
获取元素但不删除元素。如果元素被删除,会出现NoSuchElementException异常。
getFirst();
getLast();
获取元素但元素被删除。如果元素被删除,会出现NoSuchElementException异常。
removeFirst();
removeLast();
通过remove方法取出元素,不需要迭代器,例如,
while(!link.isEmpty())
{
sop(link.removeFirst());
}
LinkedList集合,JDK1.6后出现:
offerFirst();
offerLast();
获取元素但不删除元素。如果元素被删除,返回null。
peekFirst();
peekLast();
获取元素但元素被删除。如果元素被删除,返回null。
pollFirst();
pollLast();
数据结构不同,某些方法依赖的底层方法也不同,ArrayList和LinkedList中的contains、remove依靠的都是底层的equals。默认的equals方法依据对象的内存地址值来比较对象是否相同,可以根据对象的属性自建equals方法,例如,
public boolean equals(Object obj)//复写Object中的方法。
{
if(!(obj instanceof Person))//是否属于该类的对象,应该抛异常。
return false;
Person p =(Person) obj;
//System.out.println(this.name+"..."+p.name);//此句打印表示该函数被调用。
return (this.name.equals(p.name) && this.age==p.age);
}
确定使用哪种集合?元素多,频繁使用增删操作,用LinkedList;增删操作比较少的话,两者都可以;查询比较多,用ArrayList,开发中更倾向于ArrayList。
Set集合
HashSet集合
哈希表是按照哈希值来存储对象的表,通过哈希值来辨别对象。该表有自己的顺序,与存入顺序不一致。
通过toString方法和hashCode方法(返回对象哈希值)将对象的哈希值输出,也可以复写方法,自定义哈希值。默认的哈希值是内存地址值,可以根据对象的特点自建hashCode方法,如同自建equals方法。
HashSet如何保证元素的唯一性?通过hashCode和equals来完成:先是哈希值,如果不同的话直接存入元素,不调用equals;哈希值相同,再判断equals是否为true。可以复写HashCode方法,自定义哈希值方法,例如,
public int hashCode()
{
System.out.println(this.name+"..code.."+this.age);
return this.name.hashCode()+this.age*39;//避免哈希值一致。
}
获得对象独特的哈希值,避免equals判断,提高效率。创建对象都要复写hashCode和equals方法,因为对象有可能会存入HashSet集合中,底层自动调用。
只能通过迭代器取出元素。取出的元素无序排列。通过输出语句可以判断是否进入哈希表,例如,sop(hs.add("java01"));。
Set集合中,contains、remove等操作都需要在底层调用hashCode和equals。如果操作没有的元素,只调用hashCode方法,所以真正依赖的是equals方法。原则在于数据结构。与List不同。
---------------------- 黑马程序员 Android培训、期待与您交流! ---------------------