[Java]集合框架知识点总结(逐步更新)

1.迭代器

迭代器是一个接口,是一种取出集合中元素的方式。每个集合的内部以内部类的方式实现了此接口,用于取出集合的元素。这些内部类都符合迭代器接口。集合通过一个iterator方法提供这个取出元素的方式。不同的集合因为底层数据结构不同,而实现取出元素的方式不同。但都符合此接口提供的共性方法用于操纵集合元素。


常见的迭代取出方式:

public void test() {
		// TODO Auto-generated method stub
		ArrayList al = new ArrayList();
		al.add("java01");
		al.add("java02");
		al.add("java03");
		al.add("java04");

		for (Iterator it = al.iterator(); it.hasNext();) {
			System.out.println(it.next());
		}
	}

结果:

java01
java02
java03
java04


为什么定义为内部类:如果定义在外部,想操纵集合元素就要创建集合对象。所谓内部类就是一种事物内部的事物,并且这种事物可以直接访问容器中的成员,而无需创建对象。集合的内部类还妙在其实现了一个对外的接口(iterator),只要用方法返回这个内部类对象,就可用接口的共性方法实现操纵元素的目的,而无需关心其内部如何实现。

JDK的AbstractList中关于iterator的内部实现方式:

public Iterator<E> iterator() {
        return new Itr();
    }
...
private class Itr implements Iterator<E> {

 public boolean hasNext() {
            return cursor != size();
        }
...
}

2.几种不允许的和允许的泛型形式

@Test
	public void test3() {
		// 几种不允许的和允许的形式

		// 如果两边都使用泛型,形式必须一样
		// ArrayList<Object> list1 = new ArrayList<String>();//
		// 不行,前面的接收任何类型,后面的强制只接受String
		// ArrayList<String> list2 = new ArrayList<Object>();// 同样不行

		ArrayList<String> list3 = new ArrayList();// 可以,兼容性考虑,老程序使用新程序接口时

		ArrayList list4 = new ArrayList<String>();// 可以,兼容性考虑,新程序使用老程序接口时
	}

	// 老程序调用新程序
	public void bb() {
		aa(new ArrayList());
	}

	public void aa(ArrayList<String> list) {

	}

	// 新程序调用老程序
	public void dd() {
		cc(new ArrayList<String>());
	}

	public void cc(ArrayList list1) {

	}

3.HashSet:

Set集合:元素无序,不可重复。无序指存入和取出的顺序不一定一致

HashSet:底层数据结构是哈希表--->元素按哈希值存放,不按添加顺序存放,hashCode方法设置哈希值,增删改查,先比较哈希值,如果哈希值相同,再用equals比较是否是同一个对象(元素不允许重复)

例1:相同的字符串(常量)具有相同的哈希值------->即使给String对象起名字,也不过是引用

<pre name="code" class="java">public class HashSetDemo {
	public static void main(String[] args) {
		HashSet hs = new HashSet();

		hs.add("java01");// 无序,不按插入顺序
		hs.add("java03");
		hs.add("java01");

		hs.add("java02");//转成String对象---->集合只能存对象(引用)
		hs.add("java01");
		hs.add(new String("java01"));// 同样判断重复,没插进去
		hs.add(new String("java05"));

		for (Iterator it = hs.iterator(); it.hasNext();) {
			System.out.println(it.next());
		}
	}

}

 结果: 

java05java02java03java01

例2:

package it.cast.practice2;

public class Person {

	private String name;
	private int age;

	Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	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;
	}

	public int hashCode() {

		return name.hashCode() + age * 5;// name是String类型,相同字符串地址相同
	}

	// 覆写equals---->参数别写成Person,必须是Object才是复写,都是Object中的方法!
	public boolean equals(Object obj) {// 如果不覆写,会简单比较地址,都是new对象,地址当然不同,无法去除重复元素
		// 加上这一句
		if (!(obj instanceof Person))
			return false;// 直接返回false---->不能抛异常,但在内部try可以,不可以抛比父类方法更多的异常
		Person p = (Person) obj;// 必须强转,上面的参数和Object中一致


		return this.name.equals(p.getName()) && this.age == p.getAge();
	}

	public String toString() {
		return name + "..." + age;
	}
}

主程序:

public class HashSetPerson {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		HashSet hs = new HashSet();
		hs.add(new Person("lixiaoming", 35));// 要求姓名年龄相同视为同一个人
		hs.add(new Person("lixiaoming", 35));
		hs.add(new Person("lixiaoming", 35));
		hs.add(new Person("lixiaoming", 35));
		hs.add(new Person("lixiaoming", 36));
		hs.add(new Person("lixiaomin", 35));// hashCode不同直接不同
		hs.add(new Person("lixiao", 35));

		System.out.println(hs);
		System.out.println(hs.contains(new Person("lixiaomin", 35)));//先比较哈希值,相同再用equals比较,可以将equals直接return true验证
		hs.remove(new Person("lixiaomin", 35));//这个方法也是一样
		System.out.println(hs.contains(new Person("lixiaomin", 35)));
		System.out.println(hs);// 复写hashCode前,全部插入
		// hashCode直接return
		// 1,则比较hashCode相同后,用equals发现对象也相同,只插入一个!但姓名年龄不完全相同的也具有同样hashCode值不合理,应该不一样,直接不比equals了!
		// 同一个人:保证hashCode相同,并且equals返回true,那么把hashCode方法和equals都写成名字年龄组合形式,同样的名字年龄返回同样哈希值,并且equals为true.

	}

}

结果:

[lixiaoming...36, lixiaoming...35, lixiao...35, lixiaomin...35]
true
false
[lixiaoming...36, lixiaoming...35, lixiao...35]


4.TreeSet:

public class TreeSetDemo {

	public static void main(String[] args) {
		TreeSet ts = new TreeSet();

		ts.add(new Person("lisi", 29));

		System.out.println(ts);
	}

}

只插入一个没问题:

[lisi...29]

再插一个:

ts.add(new Person("zhangsan", 30));

结果:

Exception in thread "main" java.lang.ClassCastException: cn.itcast.treeset.Person cannot be cast to java.lang.Comparable
    at java.util.TreeMap.put(TreeMap.java:542)
    at java.util.TreeSet.add(TreeSet.java:238)
    at cn.itcast.treeset.TreeSetDemo.main(TreeSetDemo.java:12)

类型转换异常,无法转换成Comparable

原因:

TreeSet集合:会对元素进行排序,要求元素具有比较性,即实现Comparable接口,这种排序被称作类的自然排序

那么:修改Person类为实现Comparable接口,实现compareTo方法,按照年龄排序

package cn.itcast.treeset;

public class Person implements Comparable {//注意这里一定要实现Comparable接口!

	private String name;
	private int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public int compareTo(Object obj) {// 其实这个有泛型
		// 先判断
		if (!(obj instanceof Person)) {
			throw new RuntimeException("不是学生对象");// 不能声明!
		}
		// 强转
		Person p = (Person) obj;
		// 比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
		return this.age - p.getAge();
	}

	/**
	 * @return the name
	 */
	String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the age
	 */
	int getAge() {
		return age;
	}

	/**
	 * @param age
	 *            the age to set
	 */
	void setAge(int age) {
		this.age = age;
	}

	public String toString() {
		return name + "..." + age;
	}
}

结果:

[lisi...29, zhangsan...30]

问题:同一年龄不同姓名的按照compareTo判断相同后,不能插入,因为Set集合没有重复元素。而要求按照姓名年龄排序,也有个主次要条件的问题:先比较年龄,年龄相同比较姓名---------->注意是在compareTo中,返回负数,0,正数,而String类型已实现了Comparable接口,直接调用其compareTo方法即可排序。改进的compareTo方法:(按照姓名年龄判断和排序,返回0判定为相同元素,Set集合)

public int compareTo(Object obj) {// 其实这个有泛型
		// 先判断
		if (!(obj instanceof Person)) {
			throw new RuntimeException("不是学生对象");// 不能声明!
		}
		// 强转
		Person p = (Person) obj;
		// 比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
		// return this.age - p.getAge();
		if (this.age < p.getAge()) {
			return -1;
		}
		if (this.age == p.getAge()) {// 年龄相同的,比较姓名
			return this.name.compareTo(p.getName());
		}
		return 1;
	}

主程序:

public class TreeSetDemo {

	public static void main(String[] args) {
		TreeSet ts = new TreeSet();

		ts.add(new Person("lisi", 29));
		ts.add(new Person("lisi", 30));
		ts.add(new Person("liasi", 29));
		ts.add(new Person("liasi", 30));
		ts.add(new Person("zhangsan", 30));

		System.out.println(ts);
	}

}

结果:(年龄主要条件,姓名次要条件,姓名按照String类的自然排序)

[liasi...29, lisi...29, liasi...30, lisi...30, zhangsan...30]


问题:如果元素自身不存在比较性(无法修改),或者具备的比较性不是所需,该怎么办呢?此时就需要集合具有比较元素的功能,java提供了一个比较器用于对集合初始化,提供比较功能。

例子:

Person类保持不变,自定义比较器类:

package cn.itcast.treeset;

import java.util.Comparator;

public class MyComparator implements Comparator {// 实现Comparator接口

	// 实现其compare方法,接收两个Object类型对象,注意这个在后面也有泛型
	public int compare(Object o1, Object o2) {
		System.out.println("Hi there,Comparator");
		if (!(o1 instanceof Person) || !(o2 instanceof Person)) {
			throw new RuntimeException("非人类");
		}
		// 强转
		Person p1 = (Person) o1;
		Person p2 = (Person) o2;

		if (p1.getAge() > p2.getAge()) {
			return 1;
		}
		if (p1.getAge() == p2.getAge()) {
			return p1.getName().compareTo(p2.getName());
		}
		return -1;
	}
}

把比较器传给TreeSet容器,添加元素演示:

public class TreeSetDemo {

	public static void main(String[] args) {
		TreeSet ts = new TreeSet(new MyComparator());

		ts.add(new Person("lisi", 29));
		ts.add(new Person("lisi", 30));
		// ts.add(new Person("lisi", 30));
		ts.add(new Person("lisi", 30));
		ts.add(new Person("liasi", 29));
		// ts.add(new Person("liasi", 30));
		// ts.add(new Person("zhangsan", 30));
		// ts.add(new Person("zhangsan", 29));

		System.out.println(ts);
	}

}

结果:

Hi there,Comparator
Hi there,Comparator
Hi there,Comparator
Hi there,Comparator
[liasi...29, lisi...29, lisi...30]


(注:TreeSet元素以二叉树结构(有序)先和最小的比,找到自己位置后就不比了)

TreeSet集合比较元素,以Comparator为主(覆盖元素自身的自然排序比较方式)

练习:

需求:TreeSet存储字符串,要求以字符串长度排序

分析:字符串(String对象)本身具备比较性,但不是所需要的(并且无法更改),所以要自定义比较器传给TreeSet容器

定义前的自然排序:

public class StringLengthCompare {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TreeSet ts = new TreeSet();

		ts.add("asljfslfjslfjsl");
		ts.add("fksdf");
		ts.add("Askfhskfskfhksdfvskfv");
		ts.add("b");

		for (Iterator it = ts.iterator(); it.hasNext();) {
			System.out.println(it.next());
		}
	}

}

结果:

Askfhskfskfhksdfvskfv
asljfslfjslfjsl
b
fksdf

自定义比较器,按字符串长度排序

package cn.itcast.treeset;

import java.util.Comparator;

public class StringComparator implements Comparator {

	public int compare(Object o1, Object o2) {
		if (!(o1 instanceof String) || !(o2 instanceof String)) {
			throw new RuntimeException("非字符串类");
		}
		String s1 = (String) o1;
		String s2 = (String) o2;

		return s1.length() - s2.length();
	}
}

主程序:传入比较器初始化TreeSet集合

public class StringLengthCompare {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TreeSet ts = new TreeSet(new StringComparator());

		ts.add("asljfslfjslfjsl");
		ts.add("fksdf");
		ts.add("Askfhskfskfhksdfvskfv");
		ts.add("b");

		for (Iterator it = ts.iterator(); it.hasNext();) {
			System.out.println(it.next());
		}
	}

}

结果:

b
fksdf
asljfslfjslfjsl
Askfhskfskfhksdfvskfv


改进:如果长度相同而内容不同,则无法插入,不合理,所以增加次要排序,修改比较器:

<pre name="code" class="java">public int compare(Object o1, Object o2) {
		if (!(o1 instanceof String) || !(o2 instanceof String)) {
			throw new RuntimeException("非字符串类");
		}
		String s1 = (String) o1;
		String s2 = (String) o2;

		// return s1.length() - s2.length();
		/*
		 * int i1 = new Integer(s1.length()); int i2 = new Integer(s2.length());
		 * 
		 * if (i1 > i2) { return 1; } if (i1 == i2) { return s1.compareTo(s2);//
		 * 字符串自然顺序 } return -1;
		 */
		int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));
		if (num == 0)
			return s1.compareTo(s2);
		return num;
	}
}

 主程序: 

public class StringLengthCompare {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TreeSet ts = new TreeSet(new StringComparator());

		ts.add("asljfslfjslfjsl");
		ts.add("fksdf");
		ts.add("fksdf");
		ts.add("xksdf");
		ts.add("aksdf");
		ts.add("Bskfhskfskfhksdfvskfv");
		ts.add("Askfhskfskfhksdfvskfv");
		ts.add("b");

		for (Iterator it = ts.iterator(); it.hasNext();) {
			System.out.println(it.next());
		}
	}

}

结果:

b
aksdf
fksdf
xksdf
asljfslfjslfjsl
Askfhskfskfhksdfvskfv
Bskfhskfskfhksdfvskfv


注:Comparator可以直接在TreeSet初始化处定义为匿名内部类。


5.Map集合

示例:

import java.util.*;
class  MapDemo
{
	public static void main(String[] args) 
	{
		//HashMap与HashTable底层都是哈希表结构
		//容量 是哈希表中桶 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。
		Map<String,String> map=new HashMap<String,String>();

		System.out.println(map.put("01","zhangsan"));//返回的是其替换了的值
		System.out.println(map.put("01","zhangsan"));//key的hasCode相同
		System.out.println(map.put("01","wangwu"));//相同的键(hashCode比较),新的值会替换旧值!返回的是其替换了的值!
		map.put("02","zhangsan1");
		map.put("03","zhangsan2");
		map.put("04","zhangsan3");
		System.out.println("-----------------");

		System.out.println(map);
		System.out.println("containsKey:"+map.containsKey("01"));
		System.out.println("-----------------");

		Collection<String> coll=map.values();
		for(String s:coll)
		{
			System.out.println(s);
		}
		System.out.println("-----------------");

		//Map集合没有迭代器,其取出原理是将其转化成Set集合用迭代器
		Set<String> set=map.keySet();

		for(Iterator<String> its=set.iterator();its.hasNext();)
		{
			String key=its.next();
			String value=map.get(key);
			System.out.println(key+"="+value);
		}
		System.out.println("-----------------");

		//Map.Entry类型和迭代器实现原理相似,都是在集合类内部实现的,通过entrySet方法可以获得这个对象
		//Entry也是一个接口,它是Map接口中的一个内部接口
		/*内部实现:
		interface Map
		{
			//关系是Map内部事物,并且直接访问Map内部元素,所以定义成了内部规则(接口)
			public static interface Entry//静态,并且对外暴露,只有内部接口才能加static关键字(类成员)
			{
				public abstract Object getKey();
				public abstract Object getValue();
			}
		}

		class HashMap implements Map
		{
			class Somename implements Map.Entry
			{
				public Object getKey(){}//实现
				public Object getValue(){}//实现
			}
		}
		*/
		Set<Map.Entry<String,String>> entryset=map.entrySet();
		for(Map.Entry<String,String> entry:entryset)
		{
			System.out.println(entry.getKey()+"="+entry.getValue());
		}
		System.out.println("-----------------");
	}
}

结果输出:

D:\java\practice4>javac MapDemo.java

D:\java\practice4>java MapDemo
null
zhangsan
zhangsan
-----------------
{04=zhangsan3, 01=wangwu, 02=zhangsan1, 03=zhangsan2}
containsKey:true
-----------------
zhangsan3
wangwu
zhangsan1
zhangsan2
-----------------
04=zhangsan3
01=wangwu
02=zhangsan1
03=zhangsan2
-----------------
04=zhangsan3
01=wangwu
02=zhangsan1
03=zhangsan2
-----------------

D:\java\practice4>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值