集合简介:用于存储对象的可变长度的容器,因为容器中结构不同,所以出现了容器的体系,不断的向上抽取而成,容器体系如下图
Collection 该接口中定义了集合中的共性方法,简单列出,具体请参考对应版本JDK API.
添加 boolean add(obj)。
删除 boolean removeobj)。
是否包含元素 boolean contains(obj)。
判断是否有元素 boolean isEmpty()(判断容器中是否有元素,依据的是size方法)。
获取元素 Iterator 迭代器获取元素(迭代器,collection集合中取出元素的方法,在迭代器操作元素时,只允许进行判断hasnext,获取Next,删除remove操作),在List集合中有一个方法ListIterator()迭代器方法,可以操作
获取两个集合的交集 retainAll(Collection)。
例子:
Collection<Object> coll1=new ArrayList<Object>();
coll1.add("程序人生");
coll1.add("笑傲江湖");
coll1.add("天下第一");
coll1.add("葵花宝典");
Iterator<Object> it=coll1.iterator();
while(it.hasNext()){
//coll1.add("吸星大法");//在迭代其中,只能使用hasNext(),next(),remove 操作,
//否则则抛出java.util.ConcurrentModificationException 异步操作异常
Object s =it.next();
if(s.equals("程序人生")){
it.remove();
}
}
System.out.println("删除操作后:"+coll1);
Collection<Object> coll2=new ArrayList<Object>();
coll2.add("程序人生");
coll2.add("笑傲江湖");
coll2.add("天下第一");
coll1.retainAll(coll2);
System.out.println("交集后:"+coll1);
输出结果为:删除操作后:[笑傲江湖, 天下第一, 葵花宝典]
交集后:[笑傲江湖, 天下第一]
已知实现类介绍
List 集合特点 : 有序的,可以重复存储元素、
ArrayList :底层是基于数组结构进行存储,特点是插入,删除慢,查询快(JDK1.2)具体方法请参考对应版本API。
LinkedList :底层是基于双链表形式形式存储,特点是插入,删除快,查询慢。(JDK1.2)具体方法请参考对应版本API。
Vector : jdk1.0后的产物,线程同步,java1.2后被Arraylist取代,因为效率比较低。(知道有个这哥们就行了,已经被淘汰了)。
分析ArrayList ,从Arraylist源码可看到:初始化长度为10的数组,每次回1.5倍扩长。
//初始化定义一个长度为10的数组。
public ArrayList() {
this(10);
}
//调用参数为int型构造函数
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
this.elementData = new Object[initialCapacity];
}
为什么ArrayList操作数据的时候,增删慢,查询快,为什么LinkedList增删快,查询慢呢?
上边已经说过ArrayList是基于数组存储的,数组特点是内存空间连续的,来存储具有相同类型的元素,
数组随机访问原则:
数组随机访问:原理是寻找内存地址确认,通过下角标定位。
a[i]_address = base_address + i * data_type_size
data_type_size(字节,例如int型就是4),
通过公式和特点可看出,数组结构在查询时由于内存空间是连续的,所以根据角标可快速定位,取出数组中储存元素。查询速度快,如果数组定义长度为N,在数组为N的数组长度中插入一个数组到K的位置,K<N,此时需要把K-N位置的数据都要往后移动一位。所以导致删除,插入的速度慢。
为什么LinkedList增删快,查询慢呢?
链表结构,是一组无序的链表地址值是无序的,是通过指针将零碎的内存块链接起来。
通过LinkedList 线性表无序的特点可以看到,线性表内存地址不是连续的,所以在插入,删除的时候,时间复杂度为O(1),在进行查找的时候,根据一个一个区遍历,时间复杂度为O(n),linkedlist是基于双向链表存储
关于ListedList 单向链表和双向链表和实现方式,反转链表的实现,后续补充。
ArrayList练习:
一 :去重练习(ArrayList只要是涉及去重,则首先考虑到contains方法),去重原理
ArrayList<Object> list=new ArrayList<Object>();
list.add("广州");
list.add("上海");
list.add("北京");
list.add("杭州");
list.add("广州");
ArrayList<Object> newlist=new ArrayList<Object>();
Iterator<Object> it=list.iterator();
while(it.hasNext()){
Object obj=it.next();
if(!newlist.contains(obj)){//根据方法,新建集合是否包括此元素,不包含,则新增。
newlist.add(obj);
}
}
System.out.println(newlist);
结果如下:[广州, 上海, 北京, 杭州]
二:自定义对象去重练习
if(!newlist.contains(obj)){
newlist.add(obj);
}
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public boolean equals(Object obj) {
return (this == obj);
}
//点击contains方法,java底层封装可看出,contains以来的是equals方法进行比较,所以此处可以覆盖equals方法
操作类
/**
* 对于自定义对象去重,首先得明白ArrayList是判断什么判断是否重复数据,
* F3contains可以看出,调用的是Object父类中的equals方法,所以此处进行覆盖此方法,自定义自己比较内容即可
* @author
*/
public class ArrayListDemo {
public static void main(String[] args) {
ArrayList<Object> list=new ArrayList<Object>();
list.add(new Persons("西门吹雪", 23));
list.add(new Persons("叶孤城", 24));
list.add(new Persons("陆小凤", 25));
list.add(new Persons("花满楼", 26));
list.add(new Persons("西门吹雪", 23));
System.out.println("去重前"+list);
list=getelement(list);
}
private static ArrayList<Object> getelement(ArrayList<Object> list) {
ArrayList<Object> newlist=new ArrayList<Object>();
Iterator<Object> it=list.iterator();
while(it.hasNext()){
Object obj=it.next();
if(!newlist.contains(obj)){
newlist.add(obj);
}
}
System.out.println("去重后"+newlist);
return null;
}
对象类
public class Persons {
private String name;
private int age;
public Persons(String name,int age){
this.age=age;
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//重写Object 父类中的toString方法,方便打印,自动调用
public String toString(){
return name+":"+age;
}
重写Object 父类中的equals方法
public boolean equals(Object obj){
//此处加判断,只有是person对象才能进行转换
if(!(obj instanceof Persons)){
return false;
}
Persons p=(Persons) obj;
if(this.name.equals(p.name) && this.age==p.age){
return true;
}
return false;
}
SET
SET集合特点:无序的,数据不可重复,
hashset:底层数据结构是哈希值存储,每一个存储对象都有一个hash值。treeset为什么不能重复,底层是怎么定义的?Arraylist上述已经说过是通过Object父类中的equals进行比较,那hashmap呢?既然是哈希值存储,那会先比较哈希值,还是先比较equals地址值呢?
treeset:可以对SET集合中的元素进行排序(同种)。基于二叉树结构,为什么能够排序?原理是什么?我们可以从数据结构角度进行分析排序原理
hashset例子验证
在自定义对象类中新建
public int hashCode(){
System.out.println("hashcode");
return 1;
}
public boolean equals(Object obj){
System.out.println("equals");
return false;
}
运行主函数可发现,结果是先输出hashcode,后输出equals
由此我们可以得出结论,在hashset进行去重的时候,会调用Object 中的int hashCode方法比较哈希值,哈希值不重复的情况下,会去调用equals方法。哈希值一致是桶式链接。在Arraylist Person例子中,覆盖hashCode方法即可完成自定义对象去重。
TreeSet排序原理:
在自定义对象直接运行主函数的时候
TreeSet<Object> tree=new TreeSet<Object>();
tree.add(new Person("李四1", 12));
tree.add(new Person("李四2", 13));
System.out.println(tree);
Iterator<Object> it=tree.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
TreesetDemo.Person cannot be cast to java.lang.Comparable
运行以上代码,抛出异常Comparable
第一种实现方式,在java.lang包中有一个Comparable接口,在接口中有CompareTo方法
public class Person implements Comparable<Object>{
private String name;
private int age;
@Override
public int compareTo(Object obj) {
Person p=(Person) obj;
if(this.age>p.age){
return 1;//大于返回正整数 参考JDKAPI
}
if(this.age<p.age){
return -1;//小于返回负整数 参考JDKAPI
}
if(this.age==p.age){
return 0;//等于返回0 参考JDKAPI
}
return 0;
}
public String toString(){
return this.name+":"+this.age;
}
public Person(String name,int age){
this.age=age;
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
TreeSet<Object> tree=new TreeSet<Object>();
tree.add(new Person("西门吹雪", 31));
tree.add(new Person("叶孤城", 32));
tree.add(new Person("花满楼", 33));
tree.add(new Person("陆小凤", 34));
Iterator<Object> it=tree.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
System.out.println(tree);
}
输出结果为西门吹雪:31
叶孤城:32
花满楼:33
陆小凤:34
[西门吹雪:31, 叶孤城:32, 花满楼:33, 陆小凤:34]
2:实现自定义Comparator<Obj> 自定义比较器,通过上述例子,我们可以在treeset中实现comparable ,覆盖compareTo方法进行自定义对象的比较,实现了年龄的大小排序,如果是根据名字排序呢?如果是不同的需求根据不同排序呢,这个时候,Treeset构造方法中提供了自定义比较器,
public class CompareImpl implements Comparator<Object> {
@Override
public int compare(Object obj1, Object obj2) {
Person p1=(Person) obj1;
Person p2=(Person) obj2;
int p=p1.getName().compareTo(p2.getName());
if(p==0){
p=p1.getAge()-p2.getAge();
}
return p;
}
public static void main(String[] args) {
TreeSet<Object> tree=new TreeSet<Object>(new CompareImpl());
tree.add(new Person("叶孤城", 31));
tree.add(new Person("叶孤城", 32));
tree.add(new Person("陆小凤", 34));
Iterator<Object> it=tree.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
System.out.println(tree);
}
结果如下:叶孤城:31
叶孤城:32
叶孤城:33
陆小凤:34
[叶孤城:31, 叶孤城:32, 叶孤城:33, 陆小凤:34]
技巧:
如何判断这些容器的数据结构?
通过每一个容器的名称即可明确其数据结构:
ArrayList: 数组 array。
LinkedList: 链表:link。
HashSet: 哈希表:hash。
TreeSet: 二叉树:tree。
HashMap: 哈希表。hash。
TreeMap: 二叉树。tree。
看到array,就要想到角标。
看到link,就要想到first,last。
看到hash,就要想到hashCode,equals.
看到tree,就要想到两个接口。Comparable,Comparator(Obj)。