`
Swifly
  • 浏览: 13416 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

七、容器/集合

阅读更多
1. 容器概念:Java API 所提供的一系列类的实例,用于在程序中存放对象。
2. 容器 API:J2SDK所提供的容器API位于 java.util 包内。
    Collection 接口-定义了存取一组对象的方法,其子接口Set和List分别定义了存储方式。
        Set 中的数据对象没有顺序且不可以重复。
        List 中的数据对象有顺序且可以重复。(即互相equals)
    Map 接口定义了存储“键(key)- 值(value)映射对”的方法。


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区别:
Enumeration   Iterator   ListIterator
  不能   删除元素   删除元素
  不能   不能   增加元素
  不能   不能 反向迭代
  不支持fail-fast   支持fail-fast   支持fail-fast

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.如何选择数据结构
引用
Array读快改慢
Linked改快读慢
Hash搜索极快,遍历极慢
Tree插入/搜索都比较快,适合做索引

14. Vector & ArrayList的区别
    Vector被ArrayList代替了
    因为Vector中的方法是同步的,一般不常用,但有时在多线程中可以用。
15. hashMap和HashTable区别
Hashtable HashMap
实现了Map接口 实现了Map接口
java.lang.Object   java.util.Dictionary<K,V>       java.util.Hashtable<K,V>java.lang.Objectjava.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方法容易让人引起误解。


16. HashMap 和TreeMap区别

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

相关推荐

Global site tag (gtag.js) - Google Analytics