//集合框架内不同的集合依据其内部不同的数据结构进行区分
Iterable//(接口)(内部有返回迭代器的方法)
|--Collection //(接口)单列集合。方法:添加;删除;判断;获取(迭代器)。Itertor定义在Collection内部(Itertor是个内部类)
//迭代器与枚举的区别:(1)迭代器允许调用者利用定义良好的语义在迭代期间从迭代器所指向的 collection 移除元素;(2)方法名称得到了改进。
|--List //(接口)有序的(存取顺序一致);元素可重复;元素有角标;有ListIterator列表迭代器。
//方法:添加;删除;判断;获取;修改;listItertor(获取列表迭代器,列表迭代器在Itertor上增加了新功能)。注意:使用迭代器容易出现并发修改异常
|--ArrayList //底层数据结构为数组;查询快。不同步,不安全;可变长度数组(以50%的速度增长:*3/2+1);多了个枚举(过时了,不用)
|--Vector //底层数据结构为数组;支持枚举;效率低被ArrayList取代;同步,安全;可变长度数组(以100%的速度增长)
|--LinkedList //底层数据结构为链表;增删快。不同步,不安全
|--Set //(接口)无序的;元素不可重复。
|--HashSet //底层数据结构为哈希表(其实就是对哈希值进行存储,通过hashCode()和equals()确保元素唯一性:hasCode());使用时存储元素要重写hashCode(),equals()方法;不同步
|--LinkedHashSet //底层数据结构具有哈希表和链表的双重特性,所以有序(存取顺序一致);不同步;
|--TreeSet //底层数据结构为二叉树(这种数据结构能提高排序性能);集合中元素可排序;通过比较方法来确保元素唯一性
//使用时二种方法:让元素实现Comparable接口中的comparaTo()(自然排序);集合构造时将Comparator传入:利用compare()排序(比较器排序);线程不同步
//排序时建议使用第二种
//开发中定义类时一般复写3个方法:hashCode();equals();toString()。并实现Comparable接口(使对象具有自然排序功能)
泛型//集合中如果存储了不同类型的对象,就容易在运行时期发生类型转换异常。为了解决这个问题,就出现了泛型(JDK1.5出现的安全机制)。
泛型的好处:1,将运行时期发生的类型转换异常转移到了编译时。
2,避免了强制转换的麻烦。
泛型类的使用:当类所使用的引用类型不确定时,可以用泛型来定义。
泛型方法的使用:
泛型接口
泛型通配符:?代表任意类型
泛型的限定:? extends E(接受E类型和E的子类);? super E(接受E类型和E的父类)
注意:类中的static方法是无法访问类上的泛型的!
Map //(接口)双列集合;无序的(存取顺序不一样),要保证健的唯一性;方法:添加;删除;判断;获取;无迭代器;有个内部静态接口
|-- Hashtable //底层哈希表结构;同步的;不允许以unll为健和值;被HashMap取代
|-- Properties//很重要,健和值都是字符串,用于读写配置文件(持久化存储,与流一起使用)
String getProperty(String key);//根据指定的键获取对应的值
Object setProperty(String key, String value);//添加新元素到集合中
Set<String> stringPropertyNames()//获取Properties集合中健的Set集合
//特有方法:
//将Properties集合中的数据打印到流中
void list(PrintStream out); void list(PrintWriter out);
//将流中的信息加载到Properties集合中
void load(InputStream inStream); void load(Reader reader)
//将Properties集合中存储到流中;String comments:配置信息
void store(OutputStream out,String comments); void store(Writer out,String comments)
|-- HashMap //底层哈希表结构;不同步的;允许以NULL为健和值。
|--LinkedHashMap //底层数据结构具有哈希表和链表的双重特性,所以有序(存取顺序一致);不同步;
|-- TreeMap //底层二叉树结构;不同步的;可排序;
//集合框架工具类:工具类中方法都是静态的。
Collections //集合工具类
Arrays //数组工具类
友情提示:通过查看底层的源码可知道,不能在迭代器中对集合进行修改,否侧会报ConcurrentModificException。
关于遍历:
1,使用迭代器 Iterator : 使用于所有的Collection。因为实现了Iterable接口。
2,增强for循环 : 能使用迭代器的都可以使用
3,普通的for循环: 只用与List
4,Enumeration : 只是用于Vector
关于去重复:
1,HashSet 有去重复的功能,但是无排序的功能,它是无序存储。但是其子类中有一个可以按照录入顺序排序的类: LinkedHashSet
由于Object类中默认的hashCode()是返回int型地址,添加的对象都是一个new出现的一个地址,
系统是默认是按照地址去重复的,我们想要的是按照属性去重复,所以无法做到"按照我们需求的按属性"去重复。
所以要重写hashCode(),之后再重写equals().之所以要前比较hashCode就是为了提高效率,做一个前期的赛选工作。
2,TreeSet 使用的是二叉树的方法去重复。并对元素进行排序。
TreeSet的构造的底层实现实际上就是this(new TreeMap<E,Object>());取的就是TreeMap的key的集合
HashSet 和 HashMap 的底层关系,通过HashSet源码可以看到,HashSet的是通过new 一个HashMap实现的,只是取的是HashMap的Key值的集合。
关于排序:
1,在使用 TreeSet 时,必须指定比较的算法,指定的方式有两种:
自然顺序 : 将要存储的类实现 Comparable 接口,重写compareTo方法,在方法中指定算法。
public int compareTo(Person p){
return this.age - p.age; //升序
}
或
public int compareTo(Person p){
int ageGap = this.age - p.age;
return ageGap !=0 ? ageGap: this.name.compareTo(p.name); //升序
}
比较器顺序 : 在TreeSet构造函数中传入 Comparator,其中在compare()方法中定义算法
(优先于自然顺序:因为如果有多个TreeSet要求使用不同的排序方式时,就不能使用那一种方法了)
public static void main(String[] args){
TreeSet<Person> set = new TreeSet<Person>(new Comparator<Person>(){
public int compare(Person p1, Person p2){
int nameGap = p1.getName().compareTo(p2.getName());
return nameGap != 0 ? nameGap : p1.getAge() - p2.getAge();
}
});
set.add(new Person("scx",19));
set.add(new Person("scx",19));
set.add(new Person("ly",19));
set.add(new Person("ly",18));
System.out.println(set);
}
2, TreeMap 的排序:
面试问答:
1,hashCode方法的作用?
通过分析下面的各种情况:
Collection col = new HashSet();
Person p1 = new Person(3,3);
Person p2 = new Person(5,5);
Person p3 = new Person(3,3);
col.add(pt1);
col.add(pt2);
col.add(pt3);
col.add(pt1);
System.out.println(col.size());
情况1:若是使用ArrayList结果是4, HashSet 结果是3。
情况2:重写equals()和hashCode()方法 ,结果是2.
情况3:重写equals()和hashCode()方法 ,但是干掉hashCode().结果是3。
情况4:在106行中添加 pt1.setY = 7; col.remove(pt1); --->无法删除,会造成"内存泄漏"。
hashSet添加元素时通常是比较的是hashcode的值,也可以说是地址,因为hashcode的是根据地址来的。
要想“去重复”需要重写equals和hashCode()方法。元素存储到具有hash算法的集合中时,
此时hashCode()的值会根据hash算法得到hashCode的值,并为该值哈希值分配区域。若删除hashCode()方法
那么其hash算法仍然是按照内存地址来比较的,他们存储的内存区域就不在同一区域,就被放进去了。
为了相等的对象放在相同的区域,就得让他的hashCode相等。
当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算的哈希值的字段了,
否则,对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了。即使在contains方法使用该
对象的当前应用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,
这也会导致无法从HashSet集合中单独删除当前对象,从而造成"内存泄漏"