集合类Collection的使用

在java中,用来存放内容的容器一般为数组,但是,数组有其局限性,比如,数组的长度是不变的,存放内容的类型是固定的,因此java提供了新的容器,也就是集合

集合的概念:类似于数组,但是集合的长度不是固定的,存放的内容也不是固定的。

集合实际上是一个借口,根据底层的数据结构划分出很多子类,具体如下:


Collection就是集合类

=============================================================================================================================

首先介绍Collection中定义的方法,也就是子类中的一些共有方法:

无论是什么容器,都应该包含增,删,改,查这四个功能。

增:

add(Object obj)//在末尾加入输入的对象

addAll(Collection c)//在末尾加入一个集合

删:

boolean remove(Object obj)//删除集合中第一个obj对象,如果集合中不存在obj对象,返回false

boolean removeAll(Collection c)//把集合中与集合c内容相同的部分去掉,即使多处重复也全部去掉,没有重复返回false

clear()//把集合中所有内容都清空

改:

retainAll(Collection c)//把集合的内容变为该集合与集合c内容相同的部分

查(使用迭代器):

Iterator iterator()//用于获取迭代器

迭代器相当于一个初始指向-1位置的指针,通过移动迭代器的指向来遍历集合内容

对于迭代器对象有三个方法:hasNext()//判断是否具有下一个元素,迭代器并有因此指向下一个元素

       next()//迭代器指向下个元素,并返回下一个元素

       remove()//把迭代器当前指向的元素删除

具体使用代码如下:

import java.util.*;
class CollectionDemo 
{
	public static void main(String[] args) 
	{
		Collection c=new ArrayList();
		c.add("01");
		c.add("02");
		c.add("03");
		c.add("04");
		c.add("05");
		Iterator it=c.iterator();
		while(it.hasNext())
		{
			System.out.println(it.next());
		}
	}
}
结果:


还有一些其他的方法:

isEmpty()//判断集合是否为空,如果为空返回true,否则返回false

boolean contains(Object obj)//判断集合中是否具有指定对象

boolean containsAll(Collection c)//判断集合中是否包含指定集合

int size()//返回集合长度

==============================================================================================================================

List接口(List集合中元素是有序的,元素可以重复,因为该集合体系有索引):

在List接口下还有具体实现,当然在List也定义有其子类的共有方法:

在Collection接口的基础下,List定义了一些特有的功能,比较重要的几个如下:


get(int index)//获得指定索引位置的元素


set(int index,Object obj)//将集合中指定索引位置的内容改为obj


add(int index,Object element)//在指定索引位置处加入指定元素


addAll(int index,Collection c)//在指定索引位置处加入指定集合


remove(int index)//把集合中指定索引位置的内容删除


List subList(int begin,int end)//获取子集合,不含end位置上的内容


ListIterator listIterator()//获取列表迭代器

列表迭代器ListIterator是迭代器Iterator的子类

无论是什么迭代器,只要迭代器在作用时,就无法同时使用集合的方法去操作集合,否则会抛出ConcurrentModificationException

那么原本的迭代器Iterator只有三个基本的方法,无法实现在遍历的同时修改集合,因此java提供了ListIterator,相比于Iterator,ListIterator定义了更多的方法,如下是关键的几个方法:

set(Object obj)//把当前内容用指定对象去替代

add(Object obj)//在当前元素的后面添加指定对象内容(注意此时列表迭代器指向加入的元素位置)

ListIterator还提供了逆向遍历的功能

hasPrevious()//判断是否具有前一个元素

previous()//获得前一个元素

具体代码实现:

import java.util.*;
class CollectionDemo 
{
	public static void main(String[] args) 
	{
		List c=new ArrayList();
		ListIterator it=c.listIterator();
		it.add("01");
		it.add("02");
		System.out.println(c);
		
		while(it.hasPrevious())
		{
			System.out.println(it.previous());
		}
		it.set("03");
		System.out.println(c);
	}
}
结果:




除了这些方法外还有其他的方法,因为List集合具有索引,所以字符串的功能List集合基本都有,比如indexOf,有的名称不同,具体查阅API文档


在List接口下还分为三类:ArrayList,LinkedList,Vector

这三类是根据底层数据结构划分的

ArrayList采用数组形式存储,查询速度快,增减元素速度慢

LinkedList采用链表形式存储,查阅速度慢,但增减元素速度快

Vector也采用数组形式存储


ArrayList与Vector区别在于ArrayList不是同步的,Vector是同步的,因此Vector效率极低,因此基本不用

==============================================================================================================================

Set接口(数据的存储是无序的,所谓无序就是存的顺序和取出的顺序是没有必然联系的,而且存储的内容不能重复):

在Set接口下最重要的实现就是HashSet

HashSet的底层结构是哈希表

HashSet集合没有自己特有的功能,全部继承于Collection


HashSet集合再进行增加,修改等操作时底层都要先进行hash值的判断,如果hash值相同时,还要使用equals方法去判断内容是否也相同,从而保证集合中不会出现重复的元素,顺便说一下,如果内容相同,hash值不同并不算相同元素


以增加元素为例:执行add方法时,底层会先判断hash值,如果和集合中其他元素的hash值不同,直接就存入集合中,如果hash值和集合中的某个元素的hash值相同,那么再调用equals方法判断内容是否相同,如果不同就存入集合中,否则,存入失败,add方法返回false


由上面的解释不难看出在HashSet集合内hash值和equals方法是关键,在实际使用中,除了字符串,其他类和自定义类的equals方法都是比较地址值,因此,更多情况下,我们应该根据需要的内容来重写equals方法,比如对于Person类,实际中,可以把同名同年龄的看成同一个人,如果定义两个Person对象,而且年龄和名字一样,但是它们自动生成的hash值不一样,那么直接看成是不同的两个人,因此,应该重写equals方法和hashCode方法

注意:地址值并不是hash值,因此,hash值相同,equals(未重写)的结果也会是false


代码解释:

import java.util.*;
class Person
{
	public int age;
	public String name;
	public Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	public int hashCode()//重写hashCode方法,使得所有元素hash值相同
	{
		return 15;
	}
	/*这里重写了equals方法*/
	public boolean equals(Object obj)
	{
		if(!(obj instanceof Person))
			return false;
		Person p=(Person)obj;//不能少,因为传参使用了多态
		System.out.println(this.name+"..equals.."+p.name);
		if(this.name==p.name&&this.age==p.age)
		{
			System.out.println(this.name+"  "+this.age+"  Cannot add!!");
			return true;
		}
		else
		{
			System.out.println(this.name+"  "+this.age+"  Success!!");
			return false;
		}
	}
}
class HashSetDemo 
{
	public static void main(String[] args) 
	{
		HashSet hs = new HashSet();
		hs.add(new Person("lisi",10));
		hs.add(new Person("lisi",10));
		hs.add(new Person("zhangsan",10));
		hs.add(new Person("zhangsan",55));
		Iterator it=hs.iterator();
		while(it.hasNext())
		{
			Person p=(Person)it.next();
			System.out.println(p.name+"...."+p.age);
		}
	}
}
结果如下:


输出并不是倒序,而是乱序,只是碰巧。。


这种方法把所有hash值都统一了,即使有的元素的内容不同,也要进行equals,效率不高,如果能让hash值与内容相关,那么效率将提高


import java.util.*;
class Person
{
	public int age;
	public String name;
	public Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	public int hashCode()
	{
		return this.name.hashCode()+this.age;
	}
	public boolean equals(Object obj)
	{
		if(!(obj instanceof Person))
			return false;
		Person p=(Person)obj;//不能少,因为传参使用了多态
		System.out.println(this.name+"..equals.."+p.name);
		if(this.name==p.name&&this.age==p.age)
		{
			System.out.println(this.name+"  "+this.age+"  Cannot add!!");
			return true;
		}
		else
		{
			System.out.println(this.name+"  "+this.age+"  Success!!");
			return false;
		}
	}
}
class HashSetDemo 
{
	public static void main(String[] args) 
	{
		HashSet hs = new HashSet();
		hs.add(new Person("lisi",10));
		hs.add(new Person("lisi",10));
		hs.add(new Person("zhangsan",10));
		hs.add(new Person("zhangsan",55));
		Iterator it=hs.iterator();
		while(it.hasNext())
		{
			Person p=(Person)it.next();
			System.out.println(p.name+"...."+p.age);
		}
	}
}

结果:


不难看出调用equals方法的次数大大减少。。

============================================================================================================================

TreeSet也是Set接口下的一个实现

TreeSet集合中的内容虽然是无序的(输入输出顺序不同),但是它是根据元素内容,并按照一定规律排序的

TreeSet底层数据结构是二叉树


TreeSet保证元素不重复有两种办法,但这两种方法的实质是一样的,都是通过自行定义排序规则,当比较时出现相同时就放弃存储


方法一:让需要存储的对象的类实现Comparable接口,并重写compareTo方法,定义自己的比较规则


代码解释:

import java.util.*;
class Person implements Comparable
{
	public int age;
	public String name;
	public Person(String name,int age)
	{
		this.age=age;
		this.name=name;
	}
	public int compareTo(Object obj)//根据年龄大小升序排序,当返回0时,表示两个人是同一个人
	{
		Person p=(Person)obj;
		return this.age-p.age;
	}
}
class TreeSetDemo 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) 
	{
		TreeSet ts=new TreeSet();
		ts.add(new Person("01",10));
		ts.add(new Person("02",10));
		ts.add(new Person("03",20));
		ts.add(new Person("04",30));
		Iterator it=ts.iterator();
		while(it.hasNext())
		{
			Person p=(Person)it.next();
			sop(p.name+"...."+p.age);
		}
	}
}

结果:


不难看出02并没有传进去,这是因为判断规则只与年龄有关,但实际和名字也有关,因此,在重写规则时应注意当有多个条件时,要先判断主要条件,相同时还要判断次要条件


改进:

import java.util.*;
class Person implements Comparable
{
	public int age;
	public String name;
	public Person(String name,int age)
	{
		this.age=age;
		this.name=name;
	}
	public int compareTo(Object obj)
	{
		Person p=(Person)obj;
		int num=this.age-p.age;
		if(num==0)
			return this.name.compareTo(p.name);//字符串的equals方法返回boolean,compareTo方法放回int
		return num;
	}
}
class TreeSetDemo 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) 
	{
		TreeSet ts=new TreeSet();
		ts.add(new Person("01",10));
		ts.add(new Person("02",10));
		ts.add(new Person("03",20));
		ts.add(new Person("04",30));
		Iterator it=ts.iterator();
		while(it.hasNext())
		{
			Person p=(Person)it.next();
			sop(p.name+"...."+p.age);
		}
	}
}

结果:


ok!!


解释一下底层二叉树的存储方式:

当比较值为负数时,输入元素会走左边路径继续比较,如果没有元素可比了,就存储在该点,同理,如果为正值,则走右边路径,如果为0则放弃输入元素


给个代码:

import java.util.*;
class Person implements Comparable
{
	public int age;
	public String name;
	public Person(String name,int age)
	{
		this.age=age;
		this.name=name;
	}
	public int compareTo(Object obj)
	{
		Person p=(Person)obj;
		int num=this.age-p.age;
		if(num==0)
		{
			TreeSetDemo.sop(this.name+"...compare.."+p.name+" == "+this.name.compareTo(p.name));
			return this.name.compareTo(p.name);//字符串的equals方法返回boolean,compareTo方法放回int
		}
		TreeSetDemo.sop(this.name+"...compare.."+p.name+" == "+num);
		return num;
	}
}
class TreeSetDemo 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) 
	{
		TreeSet ts=new TreeSet();
		ts.add(new Person("01",50));
		ts.add(new Person("02",30));
		ts.add(new Person("03",60));
		ts.add(new Person("04",30));		
		ts.add(new Person("05",20));
		ts.add(new Person("06",10));
		ts.add(new Person("07",50));
		Iterator it=ts.iterator();
		while(it.hasNext())
		{
			Person p=(Person)it.next();
			sop(p.name+"...."+p.age);
		}
	}
}

结果:



二叉树存储如下:








方法二:定义一个比较器对象作为参数用来初始化TreeSet集合

具体步骤:1,自定义一个类使其实现Comparator接口,把这个类称为比较器类

  2,重写compare方法(注意不是compareTo)

  3,建立一个比较器类对象

  4,使用该对象初始化TreeSet集合


代码:

import java.util.*;
class MyComparator implements Comparator
{
	public int compare(Object obj1,Object obj2)
	{
		if(!((obj1 instanceof Person)&&(obj2 instanceof Person)))
			throw new ClassCastException();
		Person p1=(Person)obj1;
		Person p2=(Person)obj2;
		int num=p1.age-p2.age;
		if(num==0)
		{
			return p1.name.compareTo(p2.name);
		}
		return num;
	}
}
class Person
{
	public int age;
	public String name;
	public Person(String name,int age)
	{
		this.age=age;
		this.name=name;
	}
}
class TreeSetDemo 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) 
	{
		TreeSet ts=new TreeSet(new MyComparator());//用比较器初始化TreeSet
		ts.add(new Person("01",50));
		ts.add(new Person("02",30));
		ts.add(new Person("03",60));
		ts.add(new Person("04",30));		
		ts.add(new Person("05",20));
		ts.add(new Person("06",10));
		ts.add(new Person("07",50));
		Iterator it=ts.iterator();
		while(it.hasNext())
		{
			Person p=(Person)it.next();
			sop(p.name+"...."+p.age);
		}
	}
}

结果:


ok~


















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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值