Java基础16--集合框架

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xzm_rainbow/article/details/17604797

16-1,集合框架Vector集合

1,Vector是向量,底层为数组结构,可以通过其elements()方法获取一个Enumeration对象,这个对象的功能于Iterator的功能相同,Iterator比他多了移除的操作,所以现在一般不用Vector,都用Iterator。

通过Enumeration迭代Vector集合的元素示例:

public class VectorDemo {
	public static void main(String[] args) {
		Vector v = new Vector();
		v.addElement("abc1");
		v.addElement("abc2");
		v.addElement("abc3");
		v.addElement("abc4");
		Enumeration en = v.elements();
		while(en.hasMoreElements()) {
			System.out.println("nextElements:" + en.nextElement());
		}
		Iterator it = v.iterator();
		while(it.hasNext()) {
			System.out.println("next:" + it.next());
		}
	}
}

用Iterator替代Enumeration是因为Enumeration和里面的方法名字太长,简化书写。

 

16-2,LinkedList集合

1,基本操作

addFirst():向头部添加一个元素。

getFirst():获取第一个元素但不删除。

removeFirst():获取第一个元素并删除。

2,示例:

public class Demo {
	public static void main(String[] args) {
		LinkedList link = new LinkedList();
		link.addFirst("abc1");
		link.addFirst("abc2");
		link.addFirst("abc3");
		link.addFirst("abc4");
		System.out.println(link);
		System.out.println(link.getFirst());//abc4
		System.out.println(link.getFirst());//abc4,不删除
		//System.out.println(link.removeFirst());//abc4
		//System.out.println(link.removeFirst());//abc3
		while(!link.isEmpty()) {
			System.out.println(link.removeLast());//abc1,abc2,abc3,abc4
		}
	}
}

 

16-3,LinkedList练习-模拟栈和队列

需求:请使用LinkedList来模拟一个堆栈或者队列数据结构。

堆栈:先进后出。

队列:先进先出。

我们应该描述一个这样的容器,给使用者提供一个容器对象完成这两种结构中的一种。

public class LinkedTest {
	public static void main(String[] args) {
		DuiLie d1 = new DuiLie();
		d1.myAdd("abc1");
		d1.myAdd("abc2");
		d1.myAdd("abc3");
		d1.myAdd("abc4");
		while(!d1.isNull()) {
			System.out.println(d1.myGet());
		}
	}
}
class Duilie {
	private LinkedList list;
	public DuiLie() {
		list = new LinkedList();
	}
	//队列的添加元素功能
	public void myAdd(Object obj) {
		list.addLast(obj);
	}
	public Object myGet() {
		return list.removeFirst();
	}
	public boolean isNull() {
		return list.isEmpty();
	}
}

实现了队列。

 

16-4,ArrayList存储自定义对象

例如存储Person对象。

Person类:

public class Person {
	private String name;
	private int age;
	public Person() {
		super();
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return this.name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return this.age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

ArrayList存储自定义对象Person类。

public class ArrayListTest {
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		al.add(new Person("lisi1",21));
		al.add(new Person("lisi2",22));
		al.add(new Person("lisi3",23));
		al.add(new Person("lisi4",24));
		Iterator it = al.iterator();
		while(it.hasNext()) {
			//System.out.println(((Person)it.next()).getName()+"::"+((Person)it.next()).getAge());
			/*
			上面这种方法会出现错误结果,在SOP中,每next一次,就想下走一个对象,也就是说,
			第一个next读的是lisi1的name,然后跳转至下一个对象,后面的next读的是lisi2的age,
			所以出现lisi1,22,lisi3,24的结果;
			下面的方法,先创建一个对象把读取的对象赋给p,再SOP就不会出错了。
			*/
			Person p = (Person)it.next();
			System.out.println(p.getName()+"---"+p.getAge());
		}
	}
}

 

16-5,HashSet集合

1,Set集合:元素不可以重复,取出元素的顺序和存入的顺序不一定一致,是无序的。无序是因为HashSet中的元素的地址使用系统的哈希算法算出来的,哈希算法算出的位置是无序的,故无序。

Set接口中的方法和Collection一致。

    HashSet内部数据结构是哈希表,是不同步的,集合中的元素是唯一的。

示例:

public class HashSetDemo {
	public static void main(String[] args) {
		HashSet hs = new HashSet();
		hs.add("hehe");
		hs.add("heihei");
		hs.add("haha");
		hs.add("xixi");
		//hs.add("hehe");//如果有两个hehe,则最后只打印一个,因为不许重复
		Iterator it = hs.iterator();
		while(it.hasNext()) {
			System.out.println(it.next());
		}
	}
}

此程序的输出结果是无序的。

 

16-6,哈希表

1,哈希表是哈希算法,是内核的一种存储数据的算法,查找起来非常快,其底层为数组结构,哈希算法可以根据数据的属性来算出相应的在数组中的位置,直接将数据存储进去,取得时候不用遍历,将这个数据调用hashCode方法,算出位置,去相应的位置有没有该元素,就不用找其他位置的元素是否匹配了。

2,哈希表确定元素是否相同的步骤:

(1)判断的是两个元素的哈希值是否相同,如果相同,再判断两个对象的内容是否相同。

(2)判断哈希值相同,其实判断的是对象的hashCode方法,判断内容相同,用的是equals方法。

注意:如果哈希值不同,是不需要判断equals的。

 

16-7,HashSet存储自定义对象

1,如何保证HashSet的元素的唯一性呢?

是通过对象的hashCode方法和equals方法来完成确定对象的唯一性的。

如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中。

如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true,如果为true,视为相同元素,就进行存储。

记住:如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。

一般情况下,如果定义的类会产生很多对象,比如:人,学生,书,通常都需要覆盖equals,hashCode方法。

这是判断对象是否相同的依据。

2,存储自定义对象实例:

public class Person {
	private String name;
	private int age;
	public Person() {
		super();
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return this.name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return this.age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	//重写hashCode和equals方法
	@Override
	public int hashCode() {
		return name.hashCode() + age * 27;
	}
	@Override
	public boolean equals(Object obj) {
		if(this == obj) {
			return true;
		}
		if(!(obj instanceof Person)) {
			throw new ClassCastException("类型错误");
		}
		Person p = (Person)obj;
		return this.name.equals(p.name) && this.age == p.age;
	}
}

存储自定义对象类:

//往HashSet集合中存储Person对象,如果姓名和年龄相同,视为同一个人,相同元素。
public class HashSetTest {
	public static void main(String[] args) {
		HashSet hs = new HashSet();
		/*
		HashSet结合数据结构是哈希表,所以存储元素的时候,
		使用元素的hashCode方法来确定位置,如果位置相同,
		再通过元素的equals方法来确定是否相同。
		*/
		hs.add(new Person("lisi4",24));
		hs.add(new Person("lisi7",27));
		hs.add(new Person("lisi1",21));
		hs.add(new Person("lisi9",29));
		hs.add(new Person("lisi7",27));//若不重写hashCode和equals方法,这里也会输出lisi7,27,不保证唯一性
		Iterator it = hs.iterator();
		while(it.hasNext()) {
			Person p = (Person)it.next();
			System.out.println(p.getName()+"..."+p.getAge());	
		}
	}
}

 

16-8,集合框架-练习

需求:取出ArrayList中的重复元素。

public class ArrayListTest{
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		al.add("abc1");
		al.add("abc2");
		al.add("abc2");
		al.add("abc1");
		al.add("abc");
		System.out.println(al);
		al = getSingleElement(al);
		System.out.println(al);
	}
	public static ArrayList getSingleElement(ArrayList al) {
		//定义一个临时容器
		ArrayList temp = new ArrayList();
		//迭代al集合
		Iterator it = al.iterator();
		while(it.hasNext()) {
			Object obj = it.next();
			//判断被迭代到的元素是否在临时容器中存在。
			if(!temp.contains(obj)) {
				temp.add(obj);
			}
		}
		return temp;
	}
}

结果:abc1,abc2,abc2,abc1,abc

    abc1,abc2,abc

 

16-9,LinkedHashSet集合

特点:具有可迭代顺序的Set接口的哈希表,和链接列表实现。

说白了,此集合可以保证对象唯一且有序。而HashSet是无序的。

例如:

public class LinkedHashSetDemo {
	public static void main(String[] args) {
		HashSet hs = new LinkedHashSet();
		hs.add("haha");
		hs.add("hehe");
		hs.add("heihei");
		hs.add("xixi");
		Iterator it = hs.iterator();
		while(it.hasNext()) {
			System.out.println(it.next());
		}
	}
}

结果:haha hehe heihei xixi

输入和输出顺序是相同的。

 

16-10,TreeSet集合

1,这个集合可以对Set集合中的元素进行指定顺序的排序(按自然顺序进行排序)。

例如:

TreeSet ts = new TreeSet();
ts.add("zhangsan",22);
ts.add("lisi",23);
ts.add("wangwu",27);
迭代...

发现第二个ts.add报错。

原来,TreeSet要进行排序,就必须要具备排序这个功能,就Person来说,排序功能如何实现呢?

应该用java.lang中的Comparable接口,Person要想具备排序功能,实现Comparable接口即可。

Comparable接口的描述:

此接口强行对实现它的每一个类的对象进行整体排序,这种排序被称为类的自然排序,类的compareTo方法被称为他的自然比较方法。

 

2,TreeSet判断对象唯一性的方式,就是根据比较方法的返回结果是否为0,是0,就是相同元素,不存进去。在示例中,如果Person实现了Comparable接口并重写了compareTo方法,并且其返回0,则再运行时不会报错,但是只会打印出存的第一个元素,即:zhangsan,22,因为要存第二个的时候,会调用compareTo方法判断是否与上一个元素相同,由于返回结果是0,即表示相同,所以后面的元素就都不往里存了,所以只打印第一个元素。

3,以Person对象的年龄进行从小到大的排序。

将上面的Person类实现Comparable接口,然后在Person中覆写compareTo方法。

//若要按照从大到小排序,把1变为-1,把-1变为1即可,或把this变为p,把p变为this.
public int compareTo(Object obj) {
	Person p = (Person)obj;
	if(this.age > p.age)
		return 1;
	if(this.age < p.age)
		return -1;
	return 0;
}

但是如果年龄相同,则后一个就打印不出来了,即:打印时少了一个Person。

这时应该加条件,如果主要条件相同,则应该比较次要条件。

修改后的compareTo方法:

public int compareTo(Object obj) {
	Person p = (Person)obj;
	if(this.age > p.age) 
		return 1;
	if(this.age < p.age) 
		return -1;
	else
		/*
		String类中的compareTo方法也来自于Comparable接口,
		String类实现了Comparable接口
		*/
		return this.name.compareTo(p.name);
}

但是这么写烂透了...,优化:

public int compareTo(Object obj) {
	Person p = (Person)obj;
	int temp = this.age - p.age;
	return temp == 0 ? this.name.compareTo(p.name) : temp;
}

之前在TreeSet中存String类的对象,可以进行排序,是因为String类实现了Comparable接口。

 

总结:TreeSet对元素进行排序的方式之一:

让元素自身具备比较功能,元素需要实现Comparable接口,覆盖compareTo方法。

 

16-11,TreeSet集合,Comparator比较器

1,如果不要按照对象中具备的自然顺序,如果对象中不具备自然顺序,怎么办?

可以使用TreeSet的第二种排序方式:让集合自身具备比较功能。

2,TreeSet的构造器中有一个带有Comparator参数的构造器,在创建TreeSet对象时传入此Comparator的对象,该TreeSet可按指定的Comparator比较器进行排序。

3,java.util

接口:Comparator<T>

强行对某个对象Collection进行整体排序的比较函数。

4,单独创建一个Java文件,创建一个根据Person类的name进行排序的比较器。

public class ComparatorsByName implements Comparator {
	public int compare(Object o1,Object o2) {
		Person p1 = (Person)o1;
		Person p2 = (Person)o2;
		int temp = p1.getName().compareTo(p2.getName());
		return temp == 0 ? p1.getAge() - p2.getAge() : temp;
	}
}

然后,在创建TreeSet集合的时候,直接:

TreeSet ts = new TreeSet(new ComparatorsByName());

即可。

5,如果Person对象本身实现了Comparable接口,覆盖了compareTo方法,而且把Person对象存入了已经实现了比较器的TreeSet中,那么以谁的比较功能为主呢?

以比较器的功能为主,且实际开发中比较器比较常用。

实际上Java中的很多类都实现了Comparable接口,使其对象具备比较功能。

6,使集合自身具备比较功能的步骤:

定义一个类实现Comparator接口,覆盖compare方法,将该类对象作为参数传递个TreeSet集合的构造函数。

 

16-12,TreeSet-二叉树

1,TreeSet排序功能在底层是如何实现的呢?

他的排序功能在底层是通过二叉树来实现的。

以下列TreeSet的添加为例,用图表示(根据Person的age排序),

第一个进去的元素不用进行比较,以后进去的元素都要与前面的元素比较。


步骤:

第一个28进来以后,发现前面没有元素,不做比较,直接放在第一个位置。

21进来后,跟28比较,比28小,放在28的左边。

29进来后先跟28比较,比28大,放在28的右边,就不用和21比较了。

25进来后先跟28比较,比28小,到左边,再跟21比较,比21大,则放在21的右边。

......

在树中,每个节点都有自己的属性,以值为21的节点举例:


21有三个引用,分别记录与之链接的父节点,以及左子节点和右子节点,以记录整个集合各个元素的位置,但28没有父,19没有左和右。

但是树的规模一大,就涉及到效率问题,如何解决呢?

其实,只要树一排完,该集合就已经是一个有序的集合了,有序集合的查找,就可以想到用二分查找法。

其实每次向里面添加元素的时候,都会对已有的元素进行折半,二分查找,以确定新元素的位置,效率较高。

 

2,如何做到怎么存就怎么取的呢?

直接在比较器中返回1就行了,这样可以保证后进来的元素经比较后总是大于前面的元素,如果要倒序取出的话,直接返回一个-1即可。

public class ComparatorsByName implements Comparator {
	public int compare(Object o1,Object o2) {
		return 1;
	}
}

16-13,TreeSet练习

需求:按照字符串的长度排序。

public class TreeSetTest {
	public static void main(String[] args) {
		TreeSet ts = new TreeSet(new ComparatorsByLength());
		ts.add("aaaaaa");
		ts.add("zz");
		ts.add("nabq");
		ts.add("cba");
		ts.add("abc");
		Iterator it = ts.iterator();
		while(it.hasNext()) {
			System.out.println(it.next());
		}
	}
}
//比较器
public class ComparatorsByLength implements Comparator {
	public int compare(Object o1,Object o2) {
		String s1 = (String)o1;
		String s2 = (String)o2;
		int temp = s1.length() - s2.length();
		return temp == 0 ? s1.compareTo(s2) : temp;
	}
}


猜你喜欢

转载自blog.csdn.net/xzm_rainbow/article/details/17604797