1. Collection集合框架
Java.util.Collection接口
|--List子接口
|--ArrayList实现类
|--LinkedList实现类
|--Vector实现类
|--Stack(栈,继承Vector,先进后出)
|--Set子接口
|--HashSet实现类
|--TreeSet实现类
|--Queue接口(普通队列,先进先出)
|--Deque子接口(双端队列)
|--ArrayDeque实现类
2. 泛型
1)泛型是 JDK1.5引入的新特性,泛型的本质是参数化类型。在类、接口、方法的定义过程中,所操作的数据类型为传入的指定参数类型。所有的集合类型都带有泛型参数,这样在创建集合时可以指定放入集合中的对象类型。同时,编译器会以此类型进行检查。
2)ArrayList支持泛型,泛型尖括号里的符号可随便些,但通常大写E。
3)迭代器也支持泛型,但是迭代器使用的泛型应该和它所迭代的集合的泛型类型一致!
4)泛型只支持引用类型,不支持基本类型,但可以使用对应的包装类
5)如果泛型不指定类型的话,默认为Object类型。
案例6:
3. List接口和Set接口
1)List接口:List接口是Collection的子接口,用于定义线性表数据结构,元素可重复、有序的;可以将List理解为存放对象的数组,只不过其元素个数可以动态的增加或减少。
(1) List接口的两个常见的实现类:ArrayList和LinkedList,分别用动态数组和链表的方式实现了List接口。List、ArrayList和LinkedList均处于java.util包下。
(2) 可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定的差别,ArrayList更适合于随机访问,而LinkedList更适合于插入和删除,在性能要求不是特别苛刻的情形下可以忽略这个差别。
(3) 使用List我们不需要在创建的时候考虑容量集合的容量是根据其所保存的元素决定的换句话说,集合的容量是可以自动扩充的。
(4) List的实现类会重写toString方法,依次调用所包含对象的toString方法,返回集合中所包含对象的字符串表现 。
2)Set接口:Set接口是Collection的子接口,用于存储不重复的元素,元素是无序的。
(1) HashSet和TreeSet是Set集合的两个常见的实现类,分别用hash表和排序二叉树的方式实现了Set集合。HashSet是使用散列算法实现Set的。
(2) Set集合没有get(int index)方法,我们不能像使用List那样,根据下标获取元素。想获取元素需要使用Iterator。
(3) 向集合添加元素也使用add方法,但是add方法不是向集合末尾追加元素,因为无序。
4. ArrayList实现类的常用方法
(1) add(Object obj):向想集合末尾追加一个新元素,从该方法的参数定义不难看出,集合可以存放任意类型的元素,但在实际编程中我们发现,几乎不会向集合中存放一种以上的不同类型的元素。
(2) size()方法:返回当前集合中存放对象的数量。
(3) clear()方法:用于清空集合。
(4) isEmpty()方法:用于返回集合是否为空。
案例7:
(5) contains(Object obj)方法:检查给定对象是否被包含在集合中,检查规则是将obj对象与集合中每个元素进行equals比较,若比对了所有元素均没有equals为true的则返回false。注意事项:根据情况重写equals:若比较是否是同一个对象,则不需要重写,直接用contains里的equals比较即可。若重写equals为内容是否相同,则按内容比较,不管是否同一个对象。是否重写元素的equals方法对集合的操作结果有很大的效果不同!
(6) boolean remove(Object obj)方法:删除一个元素,不重写equals,不会有元素被删除(因为比较的是对象的地址,都不相同),重写equals为按内容比较,则删除第一个匹配的就退出,其他即使内容相同也不会被删除。
(7) Object remove(int index)方法:移除此列表中指定位置上的元素。向左移动所有后续元素(将其索引减1)。因此在做删除操作时集合的大小为动态变化的,为了防止漏删,必须从后往前删!
(8) Object get(int index)方法:根据元素下标获取对应位置的元素并返回。
(9) Object set(int index,Object newElement)方法:将index位置的元素修改为newElement,修改后将被修改的元素返回。可实现将List中第i个和第j个元素交换的功能:list.set ( i , list.set ( j , list.get ( i ) ) ) ;
(10) add(int index,Object newElement)方法:使用add的重载方法,我们可以向index指定位置插入newElement,原位置的元素自动向后移动,即所谓的“插队”。
(11) Object[] toArray()方法:该方法继承自Collection的方法,该方法会将集合以对象数组的形式返回。
(12) List<E> subList(int fromIndex, int toIndex)方法:获取子集合,但在获取子集后,若对子集合的元素进行修改,则会影响原来的集合。
5. HashSet实现类常用常用方法
(1) boolean add(E e) :如果此 set 中尚未包含指定元素,则添加指定元素。
(2) void clear() :从此 set 中移除所有元素。
(3) Object clone() :返回此 HashSet 实例的浅表副本:并没有复制这些元素本身。
(4) boolean contains(Object o) :如果此 set 包含指定元素,则返回 true。
(5) boolean isEmpty() :如果此 set 不包含任何元素,则返回 true。
(6) Iterator<E> iterator(): 返回对此 set 中元素进行迭代的迭代器。
(7) boolean remove(Object o): 如果指定元素存在于此 set 中,则将其移除。
(8) int size() :返回此 set 中的元素的数量(set 的容量)。
注意事项:
1)hashCode对HashSet的影响:若我们不重写hashCode,那么使用的就是Object提供的,而该方法是返回地址(句柄)!换句话说,就是不同的对象,hashCode不同。
2)对于重写了equals方法的对象,强烈要求重写继承自Object类的hashCode方法的,因为重写hashCode方法与否会对集合操作有影响!
3)重写hashCode方法需要注意两点:①与equals方法的一致性,即equals比较返回为true的对象其hashCode方法返回值应该相同(优肯不同)。②hashCode返回的数值应该符合hash算法要求,如果有很多对象的hashCode方法返回值都相同,则会大大降低hash表的效率。一般情况下,可以使用IDE(如Eclipse)提供的工具自动生成hashCode方法。
4)boolean contains(Object o)方法:查看对象是否在set中被包含。下例虽然有新创建的对象,但是通过散列算法找到了位置后,和里面存放的元素进行equals比较为true,所以依然认为是被包含的(重写equals了时)。
5)HashCode方法和equals方法都重写时对hashSet的影响:将两个对象同时放入HashSet集合,发现存在,不再放入(不重复集)。当我们重写了Point的equals方法和hashCode方法后,我们发现虽然p1和p2是两个对象,但是当我们将他们同时放入集合时,p2对象并没有被添加进集合。因为p1在放入后,p2放入时根据p2的hashCode计算的位置相同,且p2与该位置的p1的equals比较为true, hashSet认为该对象已经存在,所以拒绝将p2存入集合。
案例8:
6)不重写hashCode方法,但是重写了equals方法对hashSet的影响:两个对象都可以放入HashStet集合中,因为两个对象具有不用的hashCode值,那么当他们在放入集合时,通过hashCode值进行的散列算法结果就不同。那么他们会被放入集合的不同位置,位置不相同,HashSet则认为它们不同,所以他们可以全部被放入集合。
7)重写了hashCode方法,但是不重写equals方法对hashSet的影响:在hashCode相同的情况下,在存放元素时,他们会在相同的位置,hashSet会在相同位置上将后放入的对象与该位置其他对象一次进行equals比较,若不相同,则将其存入在同一个位置存入若干元素,这些元素会被放入一个链表中。由此可以看出,我们应该尽量使得多种类的不同对象的hashcode值不同,这样才可以提高HashSet在检索元素时的效率,否则可能检索效率还不如List。注意:两个对象值相同(x.equals(y) == true),但却可有不同的hash code。