Java基础:集合框架

第一部分:概述

一、集合类

1、面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象进行操作,就需要对对象进行存储,集合类就是存储对象最常用的一种方式。

2、集合与数组的区别:

数组虽然也可以存储对象,但长度是固定的,且数组不可能进行增删操作;

集合的长度是可变的,数组可以存储基本数据类型,集合只能存储对象。

3、集合类的特点:

集合只能存储对象,且集合的长度是可变的;集合可以存储不同类型的对象。

数据多了用对象存储,对象多了用集合存储。

二、集合框架

        容器分很多种,就有了很多共性,经过不断的向上抽取,慢慢就产生了体系 ---- 集合框架。每个容器对数据的存储方式都不同,这个存数方式称为数据结构。


第二部分:Collction集合(java.util.Collection)

Collection是集合框架的一个顶层接口,它是单列集合,该接口下有两个子接口,List与Set。

|---- Collection<E>

|---- List<E>

|---- Set<E>

一、Collection集合常用方法

Collection接口中的方法是该体系下的共性方法,常用方法如下:

1、添加元素

boolean add(E e)    添加元素

boolean addAll(Collection<? extends E> c)    将Collection集合中的所有元素都添加到该集合中

2、获取个数

int size()    获取集合中元素的个数

3、删除元素

boolean remove(Object obj)    移除集合中的指定元素

boolean removeAll(Collection<? extends E> c)    移除该集合与Collection集合的交集

void clear()    清空集合

4、判断元素

boolean isEmpty()    判断集合时候为空

boolean contains(Object obj)    判断集合是否包含指定元素

boolean containsAll(Collection<? extends E> c)    判断集合中是否包含Collection集合中的所有元素

boolean retainsAll(Collection<? extends E> c)    获取该集合与Collection集合中的交集

5、获取迭代器

Iterator<E> iterator()    获取迭代器,用于取出集合中的元素

二、迭代器的由来

1由来

        每个容器都有自己的存储和取出元素的方法,因为每个容器的数据结构不一样,所以存储和取出的动作也不一样。

        对于取出这个动作(判断是否有元素,再取),不足以用一个方法来描述,它需要用多个功能来体现,所以这多个功能定义在类中。因为取出的元素都在集合中,取出这个类就定义在了集合的内部,若想直接操作元素,定义内部类最为方便。

        而每一个容器的数据结构不同,所以取出动作的细节也不一样,但是都有判断、抽取的共性内容,那么可以将共性进行抽取,形成一个接口:Iterator。Collection集合子类中的iterator()方法返回的是Iterator的子类对象,

图例:

        

2迭代器中的方法

boolean hasNext()        如果仍有元素可以迭代,则返回 true

E next()    返回迭代的下一个元素

void remove()    从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)

示例代码:

//导入util包中的ArrayList类与Iterator
import java.util.ArrayList;
import java.util.Iterator;

public class CollectionDemo {
	public static void main(String[] args){
		//创建Collection集合的子类对象
		ArrayList<String> al = new ArrayList<String>();
	
		//向集合中添加元素
		al.add("java 01");
		al.add("java 02");
		al.add("java 03");
		al.add("java 07");
		
		//获取迭代器
		Iterator<String> it = al.iterator();
		while(it.hasNext()){
			//打印集合的元素
			sop(it.next());
		}	
	}
	
	public static void sop(Object obj){
		System.out.println(obj);
	}
}


第三部分:List集合

|---- Collection<E>

|---- List<E>    子接口,该集合中的元素是有序的,元素可以重复,该集合有索引

一、List集合中特有的方法

凡是可以操作角标的方法都是该集合中的特有方法。

1

void add(int index,E element)        在列表的指定位置插入指定元素

boolean addAll(int index,Collection<? extends E> c)    

          添加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序(可选操作)。

2

E remove(int index)        移除指定位置上的元素

3

E set(int index,E element)        用指定元素替换列表中指定位置的元素

4

E get(int index)        返回列表中指定位置的元素

List subList(int fromIndex, int toIndex)        返回列表中指定的 fromIndex(包括 )和toIndex(不包括)之间的集合

int indexOf(Object obj)        返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1

int lastIndexOf(Object obj)        返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1

ListIterator<E> listIterator()         返回此列表元素的列表迭代器

二、列表迭代器

示例代码:

package demo;
import java.util.ArrayList;
import java.util.Iterator;

public class ListIteratorDemo{
	public static void main(String[] args){
		//定义集合
		ArrayList<String> al = new ArrayList<String>();
		
		al.add("abc1");
		al.add("abc2");
		al.add("abc3");
		
		//获取迭代器
		Iterator<String> it = al.iterator();
		while(it.hasNext()){
			//在迭代的过程中使用集合的方法对集合中的元素进行增删操作
			String str = it.next();
			if(str=="abc1"){
				al.add("abc4");
			}
		}
	}
}
运行结果:


        发生了并发修改异常(ConcurrentModificationException),因为在对集合使用迭代器中的方法进行操作时,又采用了集合中的增加元素的方法,即对同一组元素进行了多种同时并发操作,发生了异常。可是Iterator的方法是有限的,只能对元素进行判断,取出和删除,如果想要其他操作,则需要使用ListIterator。


List集合特有的迭代器:ListIterator,是Iterator的子接口,其提供了以下方法:

void add(E e)    将指定的元素插入列表(可选操作)

boolean hasNext()    以正向遍历列表时,如果列表迭代器有多个元素,则返回 true

boolean hasPrevious()    如果以逆向遍历列表,列表迭代器有多个元素,则返回 true

E next()    返回列表中的下一个元素

int nextIndex()    返回对 next 的后续调用所返回元素的索引

E previous()    返回列表中的前一个元素

int previousIndex()    返回对 previous 的后续调用所返回元素的索引

void remove()    从列表中移除由 next 或 previous 返回的最后一个元素

void set(E e)    用指定元素替换 next 或 previous 返回的最后一个元素


示例代码:

import java.util.ArrayList;
import java.util.ListIterator;

public class ListIteratorDemo{
	public static void main(String[] args){
		ArrayList<String> al = new ArrayList<String>();
		
		al.add("abc1");
		al.add("abc2");
		al.add("abc3");
		
		//获取列表迭代器
		ListIterator<String> it = al.listIterator();
		while(it.hasNext()){
			//在迭代的过程中使用迭代器的方法对集合中的元素进行增删操作
			String str = it.next();
			if(str=="abc1"){
				it.add("abc4");
			}
		}
		
		System.out.println(al);
	}
}
运行结果:

三、List集合的子类

|---- List<E>    子接口

|---- ArrayList<E>::底层数据结构:数据数据结构

|---- LinkedList<E>:底层数据结构:链表数据结构

|---- Vector<E>:底层数据结构:数组数据结构


1ArrayList与LinkedList

ArrayList:查询及修改很快,增删稍慢。元素不多,有增删,也有查询操作时,用ArrayList。

LinkedList:查询很慢,增删很快。当增删特别多时,用LinkedList。

2ArrayList与Vector

ArrayList:线程不同步,效率高;默认长度为10,长度超过时以50%延长。(JDK1.2出现)

Vector:线程同步,效率低;默认长度为10,长度超过时以100%延长。被ArrayList取代。(JDK1.0出现)

3Vector集合及枚举

Vector集合有一个特有的取出方式,即枚举。通过vector中的以下方法获取。

Enumeration<E> elements()    返回此向量的组件的枚举。

Enumeration接口中有自己的取出方法

boolean hasMoreElements()    测试此枚举是否包含更多的元素。 
E nextElement()    如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。 

示例代码:

import java.util.Enumeration;
import java.util.Vector;
public class VectorDemo
{
	public static void main(String[] args)
	{
		//创建Vector集合
		Vector<String> v = new Vector<String>();

		//添加元素
		v.add("java01");
		v.add("java02");
		v.add("java03");

		//采用枚举取出元素
		Enumeration<String> en = v.elements();
		while(en.hasMoreElements())
		{
			System.out.println(en.nextElement());
		}
	}
}

4LinkedList集合

LinkedList集合中特有方法:

void addFirst(E e)        将元素添加进队列的头

void addLast(E e)        将元素添加进队列的尾

E getFirst()        获取元素,但不删除元素(若集合中无该元素,则抛出异常)

E getLast()   

E removeFirst()         获取元素,但删除元素

E removeLast()


1.6版本出现了替代方法:

E pollFirst()        获取并删除元素(若元素不存在,则返回null)

E pollLast()

E peckFirst()        获取元素但不删除元素

E peckLast()

boolean offerFirst()        添加元素

boolean offerLast()

使用LinkedList集合可以实现队列结构和堆栈结构:

队列结构:先进先出,如同一个水管

堆栈结构:先进后出,如果一个杯子

package day.day4;
/*
 * 将LinkedList封装成一个新的容器,实现队列结构与堆栈结构
 * 
 * 队列结构:先进先出
 * 堆栈结构:先进后出
 * 
 * 由于LinkedList中没有这种方法,因此自己封装好,供项目使用(基于LinkedList)
 * 
 * 向这种封装方式很常见,虽然直接可以用LinkList可以完成,但是想做成和项目相关的容器,起一些和项目相关的名称
 * 这种开发方式很常见
 */

import java.util.*;

public class MyDuiLie {
	public static void main(String[] args){
		//队列结构
		DuiLie dl = new DuiLie();
		
		dl.myAdd("java01");
		dl.myAdd("java02");
		dl.myAdd("java03");
		sop("队列结构");
		while(!dl.isNull()){
			sop(dl.myRemove());
		}
		
		//堆栈结构
		DuiZhan dz = new DuiZhan();
		
		dz.myAdd("java01");
		dz.myAdd("java02");
		dz.myAdd("java03");
		
		sop("堆栈结构");
		while(!dz.isNull()){
			sop(dz.myRemove());
		}
	}
	public static void sop(Object obj){
		System.out.println(obj);
	}
}

//基于LinkList集合定义新的集合实现队列结构
class DuiLie{
	LinkedList link;
	DuiLie(){
		link = new LinkedList();
	}
	public boolean isNull(){
		return link.isEmpty();
	}
	//存入与删除动作一致
	public void myAdd(Object obj){
		link.addLast(obj);
	}
	public Object myRemove(){
		return link.removeFirst();
	}
}
//基于LinkList集合定义新的集合实现堆栈结构
class DuiZhan{
	LinkedList link;
	DuiZhan(){
		link = new LinkedList();
	}
	public boolean isNull(){
		return link.isEmpty();
	}
	//存入与删除动作相反
	public void myAdd(Object obj){
		link.addFirst(obj);
	}
	public Object myRemove(){
		return link.removeFirst();
	}
}
运行结果:


5List集合练习

示例代码:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
 * 需求:去除List集合中的重复元素。
 * 思路:
 * 		1、定义一个新集合,遍历旧集合
 * 		2、遍历旧集合过程中判断新集合中是否包含该元素,不包含则添加
 * 		3、返回新集合
 */
public class ListDemo {
	public static void main(String[] args){
		//定义一个ArrayList集合
		ArrayList<Person> al = new ArrayList<Person>();
		//添加元素(有重复的)
		al.add(new Person("zhangsan01",20));
		al.add(new Person("zhangsan02",23));
		al.add(new Person("zhangsan01",20));
		al.add(new Person("zhangsan03",25));
		
		print(al);
		
		al = single(al);		
		print(al);
		//移除元素
		al.remove(new Person("zhangsan03",25));
	}
	
	//实现去重复元素的功能
	public static ArrayList<Person> single(ArrayList<Person> al){
		//定义新集合
		ArrayList<Person> newAl = new ArrayList<Person>();
		//对旧集合进行迭代
		Iterator<Person> it = al.iterator();
		while(it.hasNext()){
			Person p = it.next();
			//判断新集合中是否包含该元素,不包含,则添加
			if(!newAl.contains(p)){
				newAl.add(p);
			}
		}
		//返回新集合
		return newAl;
	}
	
	//对集合进行打印
	public static void print(List<Person> list){
		Iterator<Person> it = list.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
}

//对Person进行描述
class Person{
	private String name;//为节约空间,未对私有属性进行set与get
	private int age;
	
	Person(String name,int age){
		this.name = name;
		this.age = age;
	}
	//覆盖Object类中的equals方法
	public boolean equals(Object obj){
		if(!(obj instanceof Person)){
			return false;
		}
		Person p = (Person)obj;
		System.out.println(this.name+"..equals.."+p.name);
		return this.name.equals(p.name) && this.age==p.age;
	}
	public String toString(){
		return this.name +"-"+ this.age;
	}
}

运行结果:

 由结果可以看出:

        List集合判断元素是否相同(contains方法)以及删除元素(remove方法),依赖的是元素的equals方法。List集合的contains方法与remove方法在运行时会自动调用对象的equals方法,若要判断元素的内容是否相等要先覆盖Object类的equals方法。


第四部分:Set集合

|---- Collection<E>

|---- Set<E> 子接口。 元素是无序的(相对存入与取出),元素不可以重复

|---- HashSet<E>    线程不同步,效率高。哈希表

|---- TreeSet<E>     线程不同步,效率高。二叉树

一、HashSet集合

1、数据结构

HashSet集合的底层数据结构是哈希(值)表。哈希值是JVM算出来的内存地址值,每一个对象都有自己的哈希值。

2、元素唯一性

        对于HashSet集合,当数据进内存时,会先判断元素的Hash值是是否相等(hashCode方法)。若哈希值不等,则直接将元素存入集合中;若哈希值相等,则会接着判断两个对象是否相等(equals方法),不等才存入集合中。

        因此,以后描述事物时,若该事物的对象需要存到集合中,则要覆盖int hashCode()方法与boolean equals(Object obj)方法

示例代码:

/*
	需求:往HashSet集合中存入学生对象。若学生的姓名和年纪相同则视为同一对象
*/
import java.util.HashSet;

public class HashSetDemo {
	public static void main(String[] args){
		//定义HashSet集合
		HashSet<Person> hs = new HashSet<Person>();
		
		//存入元素(有重复)
		hs.add(new Person("wangwu1",20));
		hs.add(new Person("wangwu2",22));
		hs.add(new Person("wangwu2",22));
		hs.add(new Person("wangwu3",24));
		
		sop(hs);
		
	}
	public static void sop(Object obj){
		System.out.println(obj);
	}
}

class Person{
	private String name;//为节约空间,未对私有属性进行set与get
	private int age;
	
	Person(String name,int age){
		this.name = name;
		this.age = age;
	}
	//覆盖hashCode方法,
	public int hashCode(){
		return this.name.hashCode()+this.age*37;
	}
	//覆盖equals方法
	public boolean equals(Object obj){
		if(!(obj instanceof Person)){
			return false;
		}
		Person p = (Person)obj;
		return this.name.equals(p.name) && this.age==p.age;//注意判断字符串是否相等用的是equals方法,不是==
	}
	public String toString(){
		return this.name +":"+ this.age;
	}
}

运行结果:


二、TreeSet集合

1、数据结构

TreeSet集合底层数据结构是二叉树,又名红黑数。TreeSet集合中的元素是有序的

2、元素唯一性

1)元素自身基本比较性

        让元素自身具备比较性,按以下步骤执行:

(a)元素所对应的类需要实现  Comparable  接口

(b)覆盖    int compareTo(E e)    方法

        这种方式也成为元素的自然顺序或默认顺序。

 示例代码:

import java.util.TreeSet;
import java.util.Iterator;
/*
 * 需求:往TreeSet集合中存入Person对象,并按年龄进行排序
 */
public class TreeSetDemo {
	public static void main(String[] args){
		
		//第一种方式:元素自身具备比较性
		TreeSet<Person> ts = new TreeSet<Person>();
		
		ts.add(new Person("zhangsan01",20));
		ts.add(new Person("zhangsan02",24));
		ts.add(new Person("zhangsan01",22));
		ts.add(new Person("zhangsan03",23));
		ts.add(new Person("zhangsan07",23));
		
		printColl(ts);		
	}

	public static void printColl(TreeSet<Person> ts){
		Iterator<Person> it = ts.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
}
//定义Person类实现ComParable接口
class Person implements Comparable<Person>{
	private String name;//为节约空间,未对私有属性进行set与get
	private int age;
	
	Person(String name,int age){
		this.name = name;
		this.age = age;
	}
	
	//覆盖接口中的compareTo方法
	public int compareTo(Person p){
		if(this.age>p.age)
			return 1;
		//当age相同时判断name是否相同
		if(this.age==p.age)
			return this.name.compareTo(p.name);
		
		return -1;
	}
	
	public String toString(){
		return name+"-"+age;
	}
}
 运行结果:


图解:


        当二叉树中元素较多时,会从折中值开始查找。(add方法底层调用了compareTo方法,判断元素是否相等,这也是add方法返回值类型为boolean的原因)


2)集合自身具备比较性

        当元素自身不具备比较性,或者自身具备的比较性不是所需要的,这时就需要让结合自身具备比较性。步骤:

(a)定义比较器实现  Comparator  接口

(b)覆盖    int compare(E o1,E o2)    方法

(c)将比较器对象作为参数传递给TreeSet集合的构造函数

示例代码:

/*
 * 需求:定义比较器,按Person的name进行排序
 */
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetDemo {
	public static void main(String[] args){
		//定义集合,传入比较器对象,让集合自身具备比较性
		TreeSet<Person> tr = new TreeSet<Person>(new MyComparator());
		//存入元素(有重复)
		tr.add(new Person("zhangsan01",20));
		tr.add(new Person("zhangsan02",24));
		tr.add(new Person("zhangsan01",22));
		tr.add(new Person("zhangsan03",23));
		tr.add(new Person("zhangsan07",23));
		
		printColl(tr);
	}
	
	public static void printColl(TreeSet<Person> ts){
		Iterator<Person> it = ts.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
}
//描述学生
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 String toString(){
		return name+"-"+age;
	}
}

//定义比较器实现Comparator接口
class MyComparator implements Comparator<Person>{
	//覆盖compare方法
	public int compare(Person p1,Person p2){
		//主要条件:学生的名字
		int num = p1.getName().compareTo(p2.getName());
		//次要条件:学生的年龄
		if(num==0){
			return new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
		}
		
		return num;
	}
}
运行结果:

注意:

1、在使用TreeSet集合时,当主要条件相同时,一定要判断次要条件。

2、以后在描述事物时,需要覆盖hashCode()与equals方法;实现Comparable接口并覆盖compareTo方法让其具备比较性。

第五部分:泛型

一、概述

泛型是JDK1.5版本出现的新特性,用于解决安全问题(即限制所操作的类型),是一个类型安全机制。如:

TreeSet al = new TreeSet();
al.add(new Person("lisi",24));
al.add("java01");

上述代码在编译时不会报错,在运行时(会调用compareTo方法)发生类型转换异常(ClassCastException)。加入泛型后:

TreeSet<Person> al = new TreeSet<Person>();
al.add(new Person("lisi",24));
al.add("java01");

1、将运行时产生的问题ClassCastException转移到编译时期,方便程序员解决问题,让运行问题减少、安全。

2、不用再做类型强制转换动作。(Object类中的equals方法除外,因为该方法没有定义泛型)

二、泛型定义

1、基本格式

通过<>来定义要操作的引用数据类型。

泛型在集合框架中很常见,只要见到<>,就要定义泛型。

2、泛型类

1)何时定义泛型类

        当类中要操作的引用数据类型不确定时,早期定义Object来完成扩展,现在定义泛型来完成扩展。

2)示例代码

/*
	需求:演示泛型类
*/
//要操作的数据类型不确定,定义泛型来完成扩展
class Tools<QQ>
{
	private QQ q;
	public void set(QQ q)
	{
		this.q = q;
	}
	public QQ get()
	{
		return q;
	}
}
class Demo
{
	public static void main(String[] args)
	{
		//在创建对象时指定要操作的引用数据类型
		Tools<String> tl = new Tools<String>();
		tl.set("abc");
	}
}
3)泛型类的优缺点

 优点:泛型类定义的泛型,在整个类中有效。

 缺点:QQ类型只要一确定,里面的类型就确定了。(对象一建立,要操作的类型就确定了)

3、泛型方法

为了让不同的方法可以操作不同类型,且类型不确定,可以将泛型定义在方法上。

泛型方法定义格式:泛型定义在返回值的前面,修饰符的后面。

示例代码:

/*
	需求:泛型方法演示
*/
class Tools
{
	//泛型方法
	public <T> void show(T t)
	{
		System.out.println(t);
	}
	public <Q> void print(Q q)
	{
		System.out.println(q);
	}
}
class Demo
{
	public static void main(String[] args)
	{
		Tools t = new Tools();
		//调用方法时指定要操作的数据类型
		d.show("abc");
		d.print(new Integer(56));
	}
}
注意:

1、泛型类中可以再定义泛型方法。泛型类控制未定义泛型方法的成员,泛型方法控制该方法中的局部变量。

2、静态方法不可以访问类上定义的泛型。如果静态方法要操作的类型不确定,可以将泛型定义在方法上。

4、泛型接口

/*
 * 需求:演示泛型接口
 */
//泛型定义在接口上
interface Inter<T>
{
	public abstract void show(T t);
}
//子类实现时指定要操作的数据类型
class Demo implements Inter<String>
{
	public void show(String str)
	{
		System.out.println(str);
	}
}
//子类实现时要操作的数据类型不确定,定义泛型类实现
class Test<T> implements Inter<T>
{
	public void show(T t)
	{
		System.out.println(t);
	}
}

三、泛型限定

1、基本符号

?        通配符,也可以理解为占位符

? extends E        可以接收E类型或者E的子类型,上限限定

? super E        可以接收E类型或者E的父类型,下线限定

2、用法

当对象的类型不确定时,用通配符“?”表示,但不能对其类型下的数据进行具体操作,因为?表示不确定类型。

示例代码:

import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetDemo {
	public static void main(String[] args){
		
		TreeSet<String> ts1 = new TreeSet<String>();
		
		ts1.add("dian01");
		ts1.add("dian04");
		ts1.add("dian02");
		//操作String类型
		printColl(ts1);
		
		TreeSet<Integer> ts2 = new TreeSet<Integer>();
		
		ts2.add(3);
		ts2.add(5);
		ts2.add(2);
		
		//操作Integer类型
		printColl(ts2);
	}
	
	/*
	 * 定义功能,对集合中的数据进行打印
	 * 要操作的TreeSet集合的类型不确定,使用"?"完成扩展
	 * 不能在方法中对数据进行String或Integer的特有方法进行操作,因为类型不确定
	 */
	public static void printColl(TreeSet<?> ts){
		Iterator<?> it = ts.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
}
TreeSet(Collection<? extends E> c)   上限限定
TreeSet(Comparator<? super E> comparator)    下限限定(以后定义比较器时,泛型限定为父类的(super E),在定义集合时,可以往构造方法传入父类的比较器,这样就大大提高了程序的扩展性。如:
/*
	泛型限定演示

|--- Person(父类)
	|--- Student(子类)
*/
//定义父类比较器
public class MyComparator implements Comparator<Person>{
	public int compare(Person p1,Person p2){
		int num = p1.getName().compareTo(p2.getName());
		if(num==0){
			return new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
		}
		
		return num;
	}
}

class Demo{
	public static void main(String[] args){
		
		TreeSet<Student> ts = new TreeSet<Student>(new MyComparator);

		ts.add(new Student("wangwu03",24);
		ts.add(new Student("wangwu02",20);
		ts.add(new Student("wangwu01",26);

	}
}


第六部分:Map集合

一、概述

|---- Map<K,V>   接口。 K - 此映射所维护的键的类型;V - 映射值的类型

|---- HashMap<K,V>

|---- HashTable<K,V>

|---- Properties    与IO相结合的集合(无泛型)

|---- TreeMap<K,V>

Map集合时双列集合,里面存放的是键值对;一个键只能对应一个值,即要保证键的唯一性。

Collection集合是单例集合。


1、HashTable

        HashTable底层是哈希表的数据结构,该集合中不可以存入null键null值;且该集合是同步的,效率低;用作键的对象必须复写hashCode方法与equals方法。(JDK1.0出现)

2、HashMap

HashMap底层也是哈希表的数据结构,该集合可以存入null键null值;该集合不同步,效率高。(JDK1.2出现)

3、TreeMap

        底层数据结构是二叉树;线程不同步;可以用于给Map集合中的键进行排序,同样也有两种方式对元素进行比较(自然顺序,比较器)。

4、Map与Set的关系

Set集合底层使用Map集合,只是Set是单例集合,Map是双列集合。

5、Map.Entry

        Map.Entry是一个接口,表示键值对的映射关系。该接口定义在Map接口的内部,是一个内部接口,而且是静态的。该接口中有以下方法:

K getKey()        返回与此项对应的键 
V getValue()        返回与此项对应的值

二、Map集合常用方法

1、增

V put(K key,V value)        将指定的值与此映射中的指定键关联(后添加的值会覆盖原来的值,并返回被覆盖的值)

void putAll((Map<? extends K,? extends V> m)        从指定映射中将所有映射关系复制到此映射中

2、删

void clear()        清空集合

V remove(Object key)        如果存在一个键的映射关系,则将其从此映射中移除(如果没有 key 的映射关系,则返回null

3、判断(没有isEmpty方法)

boolean containsKey(Object key)        是否包含指定的键

boolean containsValue(Object value)        是否包含指定的值

4、获取

V get(Object key)        获取指定键上的值,不存在则返回null。(可用于判断一个键是否存在)

int size()        获取集合中键值映射关系数

Collection<V> value()        返回map集合中值的Collection集合

Set<K> keySet()        返回集合中键的Set集合

Set<Map.Entry<K,V>> entrySet()        返回集合中映射关系的Set集合

三、示例代码

1、通过Set集合获取集合中的元素

/*
	需求:取出集合中的元素
		  有两种方法
		  keySet
		  entrySet
*/
import java.util.*;
public class MapDemo {
	public static void main(String[] args){
		//定义HashMap集合
		Map<String,String> map = new HashMap<String,String>();
		//存入元素
		map.put("01", "guiyang");
		map.put("03", "zunyi");
		map.put("04", "zhengan");
		map.put("02", "miaotang");
		
		//Map集合的第一种取出方式(keySet)获取键的Map集合
		Set<String> set = map.keySet();
		//获取Set集合中的迭代器
		Iterator<String> it = set.iterator();
		//迭代键的Set集合
		while(it.hasNext()){
			//获取每一个键
			String key = it.next();
			//通过Map集合的get方法获取键删对象的值
			System.out.println(key+"="+map.get(key));
		}
		
		
		//第二种取出方法(entrySet)获取映射关系的Set集合
		Set<Map.Entry<String,String>> newSet = map.entrySet();
		//获取set集合对应的迭代器
		Iterator<Map.Entry<String,String>> ir = newSet.iterator();
		//对映射关系进行迭代
		while(ir.hasNext()){
			//获取映射关系
			Map.Entry<String,String> entry = ir.next();
			//通过映射关系中的getKey与getValue方法取出集合中的元素
			String key = entry.getKey();
			String value = entry.getValue();
			
			System.out.println(key+" -- "+value);
		}
	}
	
	public static void sop(Object obj){
		System.out.println(obj);
	}
}
运行结果:

2、对键进行排序

import java.util.*;
/*
 * 将学生对象及其归属地存入Map集合中
 * 按学生的年龄进行排序
 * 
 * 思路:学生对象不重复,作为键。要排序就需要用到TreeMap集合
 */
public class TreeMapDemo {
	public static void main(String[] args){
		//定义集合用于存储学生对象及其归属地
		TreeMap<Student,String> tm = new TreeMap<Student,String>();
		
		tm.put(new Student("xiaoxiao5",17), "zunyi");
		tm.put(new Student("xiaoxiao1",16), "guiyang");
		tm.put(new Student("xiaoxiao2",19), "miaotang");
		tm.put(new Student("xiaoxiao3",18), "zhengan");
		
		//取出集合中的元素
		Iterator<Student> it = tm.keySet().iterator();
		while(it.hasNext()){
			Student stu = it.next();
			String value = tm.get(stu);
			System.out.println(stu+"  "+value);
		}	
	}
}

//定义Student实现Comparable接口,让Student具备比较性
class Student implements Comparable<Student>{
	private String name;
	private int age;
	//私有成员的set与get方法、equals方法、hashCode方法由于未用到故未定义
	Student(String name,int age){
		this.name = name;
		this.age = age;
	}
	//覆盖compareTo方法
	public int compareTo(Student s){
		int num = new Integer(this.age).compareTo(new Integer(s.age));
		if(num==0)
			return this.name.compareTo(s.name);
		
		return num;
	}

	public String toString(){
		return name+" - "+age;
	}
}
运行结果:


3、集合嵌套

import java.util.*;
/*
 * 需求:集合嵌套演示
 * 定义一个Map集合czbk(学校),里面存放的是班级名称和班级
 * 班级yure是一个集合,里面存放的是学号和学生
 * 班级jiuye也是一个集合,里面存放的是学号和学生
 */
public class DoubleMap {
	public static void main(String[] args){
		//定义czbk的Map集合,用于存储班级名称和班级的集合
		TreeMap<String,TreeMap<String,Student>> czbk = new TreeMap<String,TreeMap<String,Student>>();

		//定义yure与jiuye的班级集合,里面存放的是学号与学生
		TreeMap<String,Student> yure = new TreeMap<String,Student>();
		TreeMap<String,Student> jiuye = new TreeMap<String,Student>();
		
		//向czbk中添加键(班级名称)与值(班级的集合)
		czbk.put("YuReBan", yure);
		czbk.put("JiuYeBan", jiuye);
		
		//向班级yure与jiuye中添加键(学号)与值(学生)
		yure.put("01号", new Student("zenghan01",24));
		yure.put("02号", new Student("zenghan02",22));
		
		jiuye.put("01号", new Student("diudiu01",25));
		jiuye.put("02号", new Student("diudiu02",21));
		
		//获取czbk中键(班级名称)的Set集合的迭代器
		Iterator<String> ItRoomName = czbk.keySet().iterator();
		while(ItRoomName.hasNext()){
			//依次获取班级名称
			String roomName = ItRoomName.next();
			//通过czbk获取键对应的值(班级的Map集合)
			Map<String,Student> room = czbk.get(roomName);
			System.out.println(roomName);
			//获取班级集合中键(学号)的set集合
			Iterator<String> itNum = room.keySet().iterator();
			while(itNum.hasNext()){
				//一次获取学号
				String num = itNum.next();
				//通过学号获取学生对象
				Student student = room.get(num);
				
				System.out.println(num+" "+student);
			}
		}
		
		
	}
}

//描述学生
class Student{
	private String name;
	private int age;
	Student(String name,int age){
		this.name = name;
		this.age = age;
	}
	//复写hashCode方法
	public int hashCode(){
		return name.hashCode()+age*29;
	}
	//复写equals方法
	public boolean equals(Object obj){
		if(!(obj instanceof Student))
			throw new ClassCastException();
		Student s = (Student)obj;
		return this.name.equals(s.name) && this.age == s.age;
	}
	
	public String toString(){
		return name+" - "+age;
	}
}
运行结果:

3、记录字符串中字符出现的次数

package day.day5;
import java.util.*;
/*
 * 定义一个功能,计算字符串中各个字符出现的次数
 * a(1)b(3)...
 * 
 * 思路:有映射关系---Map集合
 * 		有顺序 ------TreeMap集合
 * 
 * 将字符串转换成数组后,将每一个字符作为Key取查TreeMap集合,若有,则++,再put(该字符,次数) 没有则put该字符 ,1
 * 
 * Character自身具备比较性 (自然顺序)
 */
public class CharatorCount {
	public static void main(String[] args){
		String str = "adsfkkvczdga";
		
		String newStr = countOfChar(str);
		System.out.println(newStr);
	}
	
	public static String countOfChar(String str){
		//将字符串转换成字符数组
		char[] chs = str.toCharArray();
		//定义TreeMap集合用于存储字母与次数的映射关系
		TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
		
		//int count = 0;//定义计数器
		
		//对数组进行遍历
		for(int x=0;x<chs.length;x++){
			//拿对应字符去查TreeMap集合
			Integer value = tm.get(chs[x]);
			//元素不存在则将该字符与1存入该集合
			if(value==null){
				tm.put(chs[x], 1);
			}
			else{
				//若元素存在则次数自增后,再将该字符与对应次数存入该集合
				value++;
				tm.put(chs[x], value);
			}
			/*
			 * 替换if else
			 * if(value!=null)//如果该字符已在集合中存在,则获取次数
			 * 		count = value;
			 * count++;//不管存在不存在次数都自增
			 * tm.put(chs[x],count)//将字符与对应次数存入集合
			 * count=0;//对次数清零,否则重合
			 */
		}
		
		//定义字符串缓冲区
		StringBuilder sb = new StringBuilder();
		//获取Map集合中字符的集合
		Iterator<Character> it = tm.keySet().iterator();
		while(it.hasNext()){
			Character key = it.next();
			Integer value = tm.get(key);
			//按指定格式存入数据
			sb.append(key+"("+value+")");
		}
		//返回指定格式的数据
		return sb.toString();
	}
}
运行结果:



第七部分:Collections静态工具类

一、与Collection的区别

Collection:集合框架的一个顶层接口。

Collections:集合框架工具类。

二、常用方法

1、对List集合的排序

由于List集合中没有排序方法,Collections可以对List集合进行排序。(不能对Set集合进行排序,因为TreeSet已能排序)

public static <T extends Comparable<? super T>> void sort(List<T> list)        对list集合按自然顺序进行排序

public static <T> void sort(List<T> list,Comparator<? super T> c)        对list集合按指定比较器进行排序

2、获取最值

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 

          根据元素的自然顺序,返回给定 collection 的最大元素。(也可以指定比较器)

public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) 

          根据指定比较器产生的顺序,返回给定 collection 的最小元素。 (也可以按自然顺序)

3、折半查找(有序集合是前提)

public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) 

          使用二分搜索法搜索指定列表,以获得指定对象。不存在则返回 -(插入点)-1

public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)

          指定比较器,使用二分搜索法搜索指定列表,以获得指定对象。不存在则返回 -(插入点)-1

4、替换

public static <T> void fill(List<? super T> list, T obj)        使用指定元素替换指定列表中的所有元素

public static <T> void copy(List<? super T> dest, List<? extends T> src)       

          将所有元素从一个列表复制到另一个列表

public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)        

          使用另一个值替换列表中出现的所有某一指定值

5、比较器的强行逆转

public static void reverse(List<?> list)        反转指定列表中元素的顺序

public static <T> Comparator<T> reverseOrder() 

          返回一个比较器,它强行逆转实现了 Comparable 接口的对象 collection 的自然顺序

public static <T> Comparator<T> reverseOrder(Comparator<T> cmp)

          返回一个比较器,它强行逆转指定比较器的顺序

6、交换元素的位置

public static void swap(List<?> list, int i, int j)        在指定列表的指定位置处交换元素

public static void shuffle(List<?> list)        使用默认随机源对指定列表进行置换

public static void shuffle(List<?> list, Random rnd)        使用指定的随机源对指定列表进行置换

6、获取同步集合

给定一个线程不安全的集合,返回一个线程安全的集合:

public static <T> Collection<T> synchronizedCollection(Collection<T> c) 
public static <T> List<T> synchronizedList(List<T> list) 
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 
public static <T> Set<T> synchronizedSet(Set<T> s) 
public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m) 


第八部分:Arrays静态工具类

一、常用方法

1、折半查找(有序数组是前提)

基本数据类型的数组:

public static int binarySearch(char[] a,char key)        使用二分搜索法对key进行查找。不存在则返回 -(插入点)-1

pubilc static int binarySearch(char[] a, int fromIndex, int toIndex, char key)        指定查找的范围

引用类型数组:

public static int binarySearch(Object[] a, Object key)        元素自身具备比较器(实现Comparable接口)

public static int binarySearch(Object[] a, int fromIndex, int toIndex, Object key)

public static <T> int binarySearch(T[] a, T key, Comparator<? super T> c)         指定比较器(实现Comparator接口)

public static <T> int binarySearch(T[] a, int fromIndex, int toIndex, T key, Comparator<? super T> c)

2、排序

基本数据类型数组:

public static void sort(byte[] a)        对数组进行升序排序
public static void sort(byte[] a, int fromIndex, int toIndex)        指定排序的范围

引用类型数组:

public static void sort(Object[] a) 
public static void sort(Object[] a, int fromIndex, int toIndex)

public static <T> void sort(T[] a, Comparator<? super T> c) 
public static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c)

3、复制

基本数据类型数组:

public static int[] copyOf(int[] original, int newLength)        复制指定的数组,截取或用 0 填充(如有必要),使其具有指定长度
public static int[] copyOfRange(int[] original, int from, int to)        复制一个范围

引用类型数组:

public static <T> T[] copyOf(T[] original, int newLength)        复制指定的数组,截取或用 null 填充(如有必要)

public static <T> T[] copyOfRange(T[] original, int from, int to)        复制一个范围
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType)

public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType)

4、比较

基本数据类型数组:

public static boolean equals(double[] a, double[] a2)        比较两个数组的内容是否相等(包括顺序)

引用类型数组:

public static boolean equals(Object[] a,Object[] a2)       用equals方法来比较


深层比较:

public static boolean deepEquals(Object[] a1, Object[] a2)       Object[]中存放的可以是数组

5、替换

基本数据类型数组:

public static void fill(long[] a, long val)        用val替换数组中的每一个元素

public static void fill(long[] a, int fromIndex, int toIndex, long val)       替换一个范围

引用类型数组:

public static void fill(Object[] a, Object val)

public static void fill(Object[] a, int fromIndex, int toIndex, Object val)

6、转换成字符串

public static String toString(int[] a)        将数组中的内容转换成字符串(包含[])
public static String toString(Object[] a)        此方法等效于 Arrays.asList(a).toString()

public static String deepToString(Object[] a)    返回指定数组“深层内容”的字符串表示形式

二、集合与数组之间的转换

1、数组转换成集合

1)实现方法

public static <T> List<T> asList(T... a)        将数组转换成List集合

2)好处

        可以使用集合的思想或方法来操作数组,如判断数组中是否包含某一个元素时,使用数组需要遍历,而使用List集合时可以使用contains方法直接判断。

3)注意事项

(a)将数组变成集合,不可以使用集合的增删方法,因为数组的长度是固定的。如果使用增删操作,会发生UnspportedOperationException(不支持操作异常)。

(b)如果数组中的元素都是对象,数组中的元素就可以直接转换成为集合中的元素;如果数组中的元素是基本数据类型,那么就会将整个数组作为集合中的一个元素。

示例代码:

package demo;
/*
 * 需求:将基本数据类型数组和引用数据类型数组转换成集合
 */
import java.util.Arrays;
import java.util.List;

public class ArraysDemo{
	public static void main(String[] args){
		
		int[] arr = {1,2,3};
		String[] strs = {"java1","java2","java3"};
		Integer[] ints = {4,5,6};
		
		//将int[]转换成集合
		List<int[]> list1 = Arrays.asList(arr);
		//将String[]转换成集合
		List<String> list2 = Arrays.asList(strs);
		//将Integer[]转换成集合
		List<Integer> list3 = Arrays.asList(ints);
		
		//对集合进行打印
		print(list1);
		print(list2);
		print(list3);
	}
	public static void print(Object o){
		System.out.println(o);
	}
}
运行结果:


2、集合转换成数组

1、实现方法(Collection接口中)

Object[] toArray()        返回包含此 collection 中所有元素的数组

<T> T[] toArray(T[] a)        

        返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组a的运行时类型相同

示例代码:

package demo;

import java.util.ArrayList;
import java.util.Arrays;
/*
 * 需求:将集合转换成数组
 */
public class CollectionsDemo{
	public static void main(String[] args){
		//定义List集合并添加元素
		ArrayList<String> list = new ArrayList<String>();
	
		list.add("java01");
		list.add("java02");
		list.add("java03");
		
		String[] arr1 = new String[2];//长度小于集合的长度
		String[] arr2 = new String[6];//长度大于集合的长度
		
		//将集合转变成数组
		String[] arr3 = list.toArray(arr1);
		String[] arr4 = list.toArray(arr2);
		
		//对集合进行打印
		System.out.println(Arrays.toString(arr3));
		System.out.println(Arrays.toString(arr4));
	}	
}
运行结果:


由结果可以看出:

        当指定类型数组的长度小于集合的size,那么该方法内部会创建一个新数组,长度为集合的size;

        当指定类型数组的长度大于集合的size,就直接之用原来的数组,不再创建新数组。

因此,在定义指定类型数组时,长度为集合的size最优。

2、为何要将集合变成数组

将集合变成数组是为了限定对集合的操作,如不需要对集合元素进行增删了,就可以将该集合转换成数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值