七、容器/集合

1. 容器概念:Java API 所提供的一系列类的实例,用于在程序中存放对象。
2. 容器 API:J2SDK所提供的容器API位于 java.util 包内。
Collection 接口-定义了存取一组对象的方法,其子接口Set和List分别定义了存储方式。
Set 中的数据对象没有顺序且不可以重复。
List 中的数据对象有顺序且可以重复。(即互相equals)
Map 接口定义了存储“键(key)- 值(value)映射对”的方法。
[img]http://dl.iteye.com/upload/attachment/221357/6021223a-a8c8-3aa1-bc15-6f6d02cd7600.gif[/img]

3.Collection 接口
Collection 接口中所定义的方法:
int size();  
boolean isEmpty();
void clear();
boolean contains(Object element); //equals
boolean add(Object element);
boolean remove(Object element);
Iterator iterator();
boolean containsAll(Collection c);
boolean addAll(Collection c);
boolean removeAll(Collection c);
boolean retainAll(Collection c); //求交集
Object[] toArray();


容器类对象在调用remove、contains 等方法时需要比较对象是否相等,这会涉及到对象类型的 equals 方法和hashCode(hash容器)方法;对于自定义的类型,需要要重写equals 和 hashCode 方法以实现自定义的对象相等规则。
注意:相等的对象应该具有相等的 hash codes。
增加Name类的equals和hashCode方法如下:
public boolean equals(Object obj) {
if (obj instanceof Name) {
Name name = (Name) obj;
return (firstName.equals(name.firstName))
&& (lastName.equals(name.lastName));
}
return super.equals(obj);
}
public int hashCode() {
return firstName.hashCode();
}


4. Map 接口
实现Map接口的类用来存储键-值 对。
Map 接口的实现类有HashMap和TreeMap等。
Map类中存储的键-值对通过键来标识,所以键值不能重复。
方法:
Object put(Object key, Object value);
Object get(Object key);
Object remove(Object key);
boolean containsKey(Object key);
boolean containsValue(Object value);
int size();
boolean isEmpty();
void putAll(Map t);
void clear();


5. hashCode
hashCode重要结论:
重写equals方法,通常需要重写hashCode方法.
因为你的本意是想让两个不同的对象实现业务逻辑上的相等。但是hashMap等容器判断两个key是不是相等(重复)是靠hashCode和equals,除非hashCode也相等并且equals也相等,hashMap才会认为2个key是重复的。
如果不重写,不同的对象就不会相等。因为2个对象虽然equals是相等的。但是默认的Object类继承过来的hashCode方法将返回不同的整数
一个对象被当作HashMap里面的key的时候,hashCode和equals用来比较两个key是不是重复。即:判断key是否重复有两条件必须同时成立:第一,hash必须相等,第二,内存地址相等或者用equals判断是相等的。
jdk1.6:e.hash == hash && ((k = e.key) == key || key.equals(k))
jdk1.5:e.hash == hash && eq(k, e.key)
hashCode用来在HashMap等容器里面计算索引。目的是为了提高搜索效率。甚至比数组遍历还要快很多。

6. Set 接口
Set 接口是Collection的子接口,Set接口没有提供额外的方法,但实现 Set 接口的容器类中的元素是没有有顺序的,而且不可以重复。
Set 容器可以与数学中“集合”的概念相对应。
J2SDK API中 所提供的 Set 容器类有 HashSet,TreeSet 等。
HashSet底层靠HashMap实现的。
7. List 接口
List接口是Collection的子接口,实现List接口的容器类中的元素是有顺序的,而且可以重复。
List 容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
J2SDK 所提供的 List 容器类有 ArrayList,LinkedList 等。
List 常用算法:
void sort(List)  对List容器内的元素排序
void shuffle(List) 对List容器内的对象进行随机排列
void reverse(List) 对List容器内的对象进行逆续排列
void fill(List, Object)
用一个特定的对象重写整个List容器
void copy(List dest,List src)
将src List容器内容拷贝到dest List容器
int binarySearch(List, Object)
对于顺序的List容器,采用折半查找的方法查找特定对象


8. Comparable 接口
问题:上面的算法根据什么确定容器中对象的“大小”顺序?
所有可以“排序”的类都实现了java.lang.Comparable 接口,Comparable接口中只有一个方法 public int compareTo(Object obj);该方法: 返回 0 表示 this == obj 返回正数表示 this > obj 返回负数表示 this < obj
实现了Comparable 接口的类通过实现 comparaTo 方法从而确定该类对象的排序方式。

9. Iterator 接口
所有实现了Collection接口的容器类都有一个iterator方法用以返回一个实现了Iterator接口的对象。
Iterator对象称作迭代器,用以方便的实现对容器内元素的遍历操作。
Iterator接口定义了如下方法:
boolean hasNext(); //判断游标右边是否有元素
Object next(); //返回游标右边的元素并将游标移动到下一个位置
void remove(); //删除游标左面的元素,在执行完next之后该操作只能执行一次
import java.util.*;
public class Test {
public static void main(String[] args) {
Collection c = new HashSet();
c.add(new Name("f1","l1"));
c.add(new Name("f2","l2"));
c.add(new Name("f3","l3"));
Iterator i = c.iterator();
while(i.hasNext()) {
//next()的返回值为Object类型,需要转换为相应类型
Name n = (Name)i.next();
System.out.print(n.getFirstName()+" ");
}
}
}

Enumeration/ Iterator/ ListIterator区别:
[table]
|| Enumeration || Iterator || ListIterator
|| 不能 || 删除元素 || 删除元素
|| 不能 || 不能 || 增加元素
|| 不能 || 不能 || 反向迭代
|| 不支持fail-fast || 支持fail-fast || 支持fail-fast

[/table]
10. 补充:JDK1.5增强的for循环
增强的for循环对于遍历array 或 Collection的时候相当简便;
缺陷:
数组:不能方便的访问下标值;
集合:与使用Iterator相比,不能方便的删除集合中的内容。
总结:除了简单遍历并读出其中的内容外,不建议使用增强for。
import java.util.*;

class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
for(int a : arr) {
System.out.println(a);
}

Collection c = new ArrayList();
c.add(new String("aaa"));
c.add(new String("bbb"));
c.add(new String("ccc"));
for(Object o : c) {
//c.remove(o);
System.out.println(o);
}
}
}

11. Auto-boxing/unboxing
在合适的时机自动打包、解包
自动将基础类型转换为对象
自动将对象转换为基础类型
//注意i1和i2都是Integer类型,事实上只要这个值的范围在“-128—127”之间,输出结果都是“Equal!”。

//当i1和i2值为128时,在进行 “==”时,它们被装进两个不同的Integer Objects,
//由于这是两个不同的instances,它们引用不同的内存地址,所以结果是“Not equal!”。

//对于值从-128到127之间的值,它们被装箱为Integer对象后,会存在内存之中被重用
//这样进行“==”时,JVM仍然使用的是相同的object instance,所以输出结果为“Equal!”了。

//如果超过了从-128到127之间的值,被装箱后的Integer对象并不会被重用,
//即相当于每次都新建一个Integer对象,所以当值在 200,使用'=='进行比较时,i1与i2参考的是不同的对象。


class Test {
public static void main(String[] args) {
Integer i1 = 127;

Integer i2 = 127;

i1 = 126;

if (i1 == i2)
System.out.println("Equal!");
else
System.out.println("Not equal!");
}
}

12. 补充:JDK1.5泛型

//等号左边可以不写泛型,
//如果方法返回值是泛型,那么返回值是Object,
//方法参数如果是泛型javac不再做类型检查
//-->索引这样写代码不可取
//等号右边可以不写泛型,默认是调用方法时候,靠左边的泛型来限制,而且取出来的是泛型的类型
//因为等号右边可能是以前定义的类
//不要这样写代码
//如果都写泛型,左右必
import java.util.*;
class Test{
public static void main(String [] args){
Collection<String> a = new ArrayList<String>();
a.add("aaa");
a.add("bbb");
a.add("ccc");
a.add("ddd");
//a.add(new Name("sss"));
System.out.println(a);
System.out.println("------------------------------");

Myclass<String> my = new Myclass<String>();
my.setName("sdf");
Myclass<A> my1 = new Myclass<A>();
//Myclass<A> my1 = new Myclass<AA>(); X
//Myclass<AA> my1 = new Myclass<A>(); X
//Myclass<A> my1 = new Myclass<B>(); X
my1.setName(new A());
}
}
class Name{
String name;
Name(String name){
this.name = name;
}
public String toString(){
return name;
}
}
class Myclass<T> {
T name;
public void setName(T name){
this.name = name;
}
}
class A{
}
class AA extends A{
}
class B{
}

13.如何选择数据结构
[quote]Array读快改慢
Linked改快读慢
Hash搜索极快,遍历极慢
Tree插入/搜索都比较快,适合做索引
[/quote]
14. Vector & ArrayList的区别
Vector被ArrayList代替了
因为Vector中的方法是同步的,一般不常用,但有时在多线程中可以用。
15. hashMap和HashTable区别
[table]
||Hashtable ||HashMap
||实现了Map接口 ||实现了Map接口
||java.lang.Object
java.util.Dictionary<K,V>
java.util.Hashtable<K,V>||java.lang.Object
java.util.AbstractMap<K,V>
java.util.HashMap<K,V>

||Hashtable中的方法是同步的, ||HashMap是Hashtable的轻量级实现(非线程安全的实现)。
在多线程应用程序中,需要额外的同步机制。
||  ||但HashMap的同步问题可通过Collections的一个静态方法得到解决:
Map Collections.synchronizedMap(Map m)
 这个方法返回一个同步的Map,这个Map封装了底层的HashMap的所有方法,使得底层的HashMap即使是在多线程的环境中也是安全的。
||Hashtable不允许。  ||在HashMap中,null可以作为键,这样的键只有一个;
 可以有一个或多个键所对应的值为null。
 当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
||HashTable使用Enumeration, ||HashMap使用Iterator。
||HashTable中hash数组默认大小是11,增加的方式是 old*2+1。 ||HashMap中hash数组的默认大小是16,而且一定是2的指数。
||哈希值的使用不同,HashTable直接使用对象的hashCode, ||而HashMap重新计算hash值,而且用与代替求模:
|| ||由于非线程安全,效率上可能高于Hashtable。
|| ||HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。

[/table]

16. HashMap 和TreeMap区别

[table]
|| ||HashMap ||TreeMap
||key有无顺序 ||HashMap通过hashcode对其内容进行快速查找(HashMap中元素的排列顺序是不固定的)。 ||而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap
||判断key是否重复逻辑不同 ||判断key是否重复有两条件必须同时成立:第一,hash必须相等,第二,内存地址相等或者用equals判断是相等的。 ||compareTo方法比较是0
||对key类的要求不同 ||key类重写equals有必要重写hashCode方法|| Key类必须实现了Comparable接口。

[/table]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值