您的位置:首页 > 编程语言 > Java开发

常见java集合的实现细节

2016-04-10 15:02 585 查看
一、set 和map 

set 代表一种集合元素无序、集合元素不可重复的集合,map则代表一种由多个key-value对组成的集合,map集合类似于传统的关联数组。表面上看它们之间相似性很少,但实际上两者之间有莫大的关联,可以说,map是set的扩展。

1.1 set 和map的关系

对比两者的继承类体系,可以发现有相似的地方





这些Map集合的key具有一个特征:所有key不能重复,key之间没有顺序。也就是说,如果将Map集合的所有key集中起来,那这些key就组成了一个set集合。所以,发现map集合提供了如下方法来返回所有key组成的set集合。

set<k>  KeySet ()

由此可见,map集合的所有key将具有set集合的特征,只要把map的所有key集中起来看,那它就是一个set,实现了从map到set的转换。

换一种思维来理解map集合,如果把map集合中的value当成key的“附属物”,那么map集合在保存key-value对时只考虑key即可。

对于一个map集合而言,它本质上是一个关联数组。

下面为map集合的示意图



一个是key数组,一个是value数组,如果将key和与之对应的value绑定,就可以使用一个set集合来保存。

1.2  HashMap 和 HashSet

实际上,这两者之间有很多相似之处。对于hashset,系统采用hash算法决定集合元素的存储位置,这样可以保证快速存,取集合元素;对于HashMap而言,系统将value当成key的附属,系统根据hash算法来决定key的存储位置,这样可以保证快速存,取key,而value总是紧随key存储。

虽然集合号称存储的是java对象,但实际上并不会真正将java对象放入set集合中,而只是在set集合中保留这些对象的引用而已。也就是,java集合实际上是多个引用变量所组成的集合,这些引用变量指向实际的java对象。

对于每个java集合来说,其实只是多个引用变量的集合。下面程序证明

import java.util.List;

import java.util.ArrayList;

class apple

{
double weight;
public apple(double weight)
{
this.weight=weight;
}

}

public class inittest {

public static void main(String[] args)

{
apple t1=new apple(1.3);
apple t2=new apple(2.3);
List<apple> list=new ArrayList<apple>(4);
list.add(t1);
list.add(t2);
System.out.println(list.get(0)==t1);
System.out.println(list.get(1)==t2);

}

}

程序输出结果为 

true

true

程序执行时,内存分配情况如下,



从中可看出,arraylist中存储的是对象的引用,而不是对象本身,此时长度我们给定4个长度,如果不给,系统默认为10,

对于hashmap而言,它的存储方式较arraylist复杂些,采用一种hash算法,来决定每个元素的存储位置。

HashMap<String, double> map= new HashMap<String,double>();

map.put("语文“,80.0);

....

当程序执行map.put时,系统调用”语文“的hashcode()方法得到其hashcode值,每个java对象都有hashcode()方法,都可以得到hashcode值,然后系统根据这个值来决定该元素的存储位置。

当向hashmap中添加key-value对,由其key的hashcode()返回值决定该key-value对(即entry)的存储位置。当两个entry对象的key的hashcode值相等时,将由key通过eqauls()比较值决定是采用覆盖行为,还是产生entry链。

当创建一个hashmap时,系统会创建一个table数组来保存hashmap中的entry,数组大小为找出大于initialcapacity,最小的2的n次方,hashmap包含如下几个构造器

hashmap();

hashmap(int initialcapacity);

hashmap(int initialcapacity,float loadfactor);

所以,创建hashmap时指定的容量并不是等于实际的容量。



上面是一个table数组,每一个格叫bucket,用来存储元素(entry)的位置,hashmap之所以能快速存,取的原因是因为:不同的东西放在不同的地方,需要时才能快速找到!

hashset的绝大部分方法都是通过调用hashmap的方法来实现的,因此hashset,hashset 两个集合本质上是相同的。

import java.util.Set;

import java.util.HashSet;

class name

{
private String first;
private String last;
public name(String first,String last)
{
this.first=first;
this.last=last;
}
public boolean equals(Object o)
{
if(this==o)
{
return true;
}
if(o.getClass()==name.class)
{
name n=(name)o;
return n.first.equals(first)&&n.last.equals(last);
}
return false;
}

}

public class inittest {

public static void main(String[] args)

{
Set<name> s=new HashSet<name>();
s.add(new name("abc","123"));
System.out.println(s.contains(new name("abc","123")));

}

}

程序结果为false

上面程序向hashset添加一个对象之后,立即通过程序判断该hashset中是否有这个对象,结果是没有。这是因为hashset判断两个对象是否相等的标准除了要求通过equals返回true之外,还要求两个hashcode返回值相等。上面程序没有重写hashcode方法。所以,当试图把某个类的对象当成hashmap的key,或者试图将这个类的对象放入hashset中保存时,重写该类的equals()和hashcode()很重要,返回值必须相等。

1.2 treeset 和treemap 

treeset 底层实际使用的存储容器就是treemap,对于treemap而言,采用的是红黑树的排序二叉树来保存map中的每一个entry,每一个entry被当成一个节点对待。

 

1.3 map 和list的关系

map接口提供get(K key)方法允许map对象根据key来获取value;

list接口提供了get( int  index)方法允许list对象根据元素索引来获取value;

java要求各种集合都提供一个iterator()方法,该方法可以返回一个iterator用于遍历该集合中元素,至于返回的iterator到底是哪儿种实现类,程序并不关心,这就是典型的”迭代器模式“
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: