JAVA集合Collection(集合框架)分析

集合简介:用于存储对象的可变长度的容器,因为容器中结构不同,所以出现了容器的体系,不断的向上抽取而成,容器体系如下图

1

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是基于数组存储的,数组特点是内存空间连续的,来存储具有相同类型的元素,

数组随机访问原则:

2

数组随机访问:原理是寻找内存地址确认,通过下角标定位。

   a[i]_address = base_address + i * data_type_size

   data_type_size(字节,例如int型就是4),

通过公式和特点可看出,数组结构在查询时由于内存空间是连续的,所以根据角标可快速定位,取出数组中储存元素。查询速度快,如果数组定义长度为N,在数组为N的数组长度中插入一个数组到K的位置,K<N,此时需要把K-N位置的数据都要往后移动一位。所以导致删除,插入的速度慢。

为什么LinkedList增删快,查询慢呢?

链表结构,是一组无序的链表地址值是无序的,是通过指针将零碎的内存块链接起来。

 

1

通过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)。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值