集合深入理解
集合:用于存储对象,长度可变,可存储不同类型元素
框架结构:
Collection:
Set:无序,元素不可重复
常见子类:
| ——HashSet 底层数据结构是哈希表
1, 通过hashCode和equals方法判断对象是否相等
2,如果元素hashCode值不同,不会调用equals方法
3,HashCode值相同才会接着调用equals方法去判断
|——TreeSet 底层数据结构是二叉树
可以对Set集合元素进行排序
List:有序,元素可重复,因为该集合有索引
|—— ArrayList:底层是数组结构,查询速度快。线程不同步,效率高。初始长度10
可变长度原理:通过不断new数组,长度是50%延长,将原数组copy到新数组,再接着添加新元素
|—— LinkedList:底层是链表,增删数度快,查询慢
|—— Vector:底层是数组结构。线程同步。延长100%
Java1.6新加入了Deque来代替Vector下的Stack类
Map 以键值对的形式存值
|—— HashTable 底层是哈希表结构,不可存入null键null值,线程同步
|—— HashMap 底层是哈希表结构,允许使用null值和null键,不同步,效率高
|—— TreeMap 二叉树,红黑树,不同步,根据键 排序
Collections
工具类,不提供构造函数,不能创建对象,提供static方法对集合进行操作
如:排序,synchronizedListList()方法,将普通集合包装成线程安全的
Set和Map的关系:
Map的所有键key集中在一起就是一个Set集合,Map集合中有一个keySet()方法,就是返回key所组成的Set集合
如果将value值看作key的一个附属属性的话,key-value组合起来,也是形成一个Set集合,Map.Entry是一个内部接口
Set集合如果存入的数据是Map.Entry形式,可以扩展成Map集合
注意:虽然说集合中存储的是java对象,但是实际上集合中存储的还是对对象的引用
如下:
import java.util.ArrayList;
import java.util.List;
class Apple {
private double weight;
public Apple(double weight) {
this.weight = weight;
}
}
public class ListTest {
public static void main(String[] args) {
Apple a = new Apple(1.1);
Apple a2 = new Apple(2.2);
List<Apple> list = new ArrayList<Apple>();
list.add(a);
list.add(a2);
// 输出true说明集合中的变量和a引用指向同一个对象
System.out.println(list.get(0) == a);
}
}
HashMap和HashSet
向hashSet集合中装东西时,重写equals方法和hashCode方法非常重要
如下:
import java.util.HashSet;
class Stuff {
private String id;
private int age;
public Stuff(String id, int age) {
this.id = id;
this.age = age;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj.getClass() == Stuff.class) {
Stuff s = (Stuff) obj;
return s.id.equals(id);
}
return false;
}
public int hashCode() {
// id唯一,根据返回id属性的哈希值
return id.hashCode();
}
public String toString() {
return "id:" + id + ",age:" + age;
}
}
public class HashSetTest {
public static void main(String[] args) {
HashSet<Stuff> hs = new HashSet<Stuff>();
hs.add(new Stuff("ks123", 22));
// 根据自己写的hashCode方法,此处会判断第二个对象和第一个对象一样,所以不能重复添加
hs.add(new Stuff("ks123", 23));
System.out.println(hs);
}
}
Map和List
Map集合中的value集中起来就是一个list集合,通过map.values()方法可得到所有value数据
Values()源代码
public Collection<V> values(){
// 获取values实例变量
Collection<v> vs = values;
// 如果vs == null,将返回new Values()对象
return (vs!=null?vs:new Values());
}
可以看出,返回的并不是list集合对象
ArrayLsit和LinkedList
ArrayList的性能总体上来说还是高于LinkedList
在查询元素时,因为ArrayList是数组结构,有索引,比较好找
在删除和增加元素时,ArrayList需要对元素整体移动,所以LinkedList更优
迭代器Iterator接口
Java要求各种集合都提供一个iterator()方法,该方法返回一个Iterator用于遍历集合中的元素,至于返回的Iterator到底是哪种实现类,程序并不关心,这就是”迭代器模式”;
如:Iterator和Enumeration两个接口都是迭代器模式的代表作,他们就是“迭代器接口”。
迭代器模式就是指:系统为遍历多种数据列表,集合,容器提供一个标准的“迭代器接口”,这些数据列表,集合,容器就可面向相同的“迭代器接口”编程,通过相同的迭代器接口访问不同的数据列表,集合,容器里的数据。不同的数据列表,集合,容器如何实现这个“迭代器接口”,则交给个数据列表,集合,容器自己完成
迭代时删除指定元素
由于Iterator迭代器只负责迭代,自己并没有保留集合元素,所以通常迭代时不应该删除集合元素,但是java允许通过Iterator的remove()方法删除刚刚迭代的元素。
实际上在某些特殊情况下,可以在使用Iterator迭代集合时直接删除集合中某个元素
如下:在迭代倒数第二个元素时,可以删除该元素,但是删除其他元素就会抛异常
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayListRemove {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("itheima");
list.add("cloud");
list.add("thythm");
list.add("zhangsan");
for (Iterator<String> it = list.iterator(); it.hasNext();) {
String ele = it.next();
System.out.println(ele);
if ("thythm".equals(ele)) {
// 直接删除集合中的倒数第二个元素
list.remove(ele);
}
}
}
}
对于TreeSet,HashSet等Set集合而言,当使用Iterator遍历他们时,如果正在遍历最后一个集合元素,那么使用Set集合的remove()方法删除集合的任何元素不会引起异常
实际上此时的删除动作已经在遍历操作的范围外了