Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java高级特性_1 集合框架及泛型 #36

Open
Qingquan-Li opened this issue Feb 23, 2017 · 0 comments
Open

Java高级特性_1 集合框架及泛型 #36

Qingquan-Li opened this issue Feb 23, 2017 · 0 comments
Labels

Comments

@Qingquan-Li
Copy link
Owner

Qingquan-Li commented Feb 23, 2017

参考:
https://docs.oracle.com/javase/8/docs/technotes/guides/collections/overview.html
http://docs.oracle.com/javase/8/docs/api/
http://www.javatpoint.com/collections-in-java
http://www.runoob.com/java/java-collections.html

为什么使用集合框架?

开发应用程序中,如果想储存多个不同类型的数据,可以用数组来实现,但采用数组存在一些明显的缺陷:

  • 数组长度固定不变,不能很好适应元素数量动态变化的情况;
  • 可通过 数组名 .length 获取数组长度,却无法直接获取数组中实际储存的元素个数;
  • 数组采用在内存中分配连续空间的储存方式,根据元素信息查找时效率低,需要多次比较。

Java 集合框架提供了一套性能优良、使用方便的接口和类,它们位于 java.util 包中。

Java 集合框架结构图

  1. 虚线框表示接口(或者抽象类),实线框表示实现类,粗实线框表示开发中常用的类。
    Java 的集合类主要由 Collection 接口和 Map 接口派生而来,其中 Collection 接口有两个常用的子接口( ListSet )。所以通常说 Java 集合框架由三大类接口构成( List 接口、 Set 接口、 Map接口)。
  2. 其中, Iterator 接口表示对集合进行迭代的迭代器。 Iterator 接口为集合而生,专门实现集合的遍历。
  3. 除了集合,该框架也定义了 Map 接口及其实现类。Map 里存储的是键/值对。尽管 Map 不是 Collections,但是它们完全整合在集合中。
  4. 为了方便的对 Collection 对象、 Array 对象进行操作,Java中提供了 Collections 工具类(集合操作工具类)和 Arrays 工具类对其进行操作。
    其中 Arrays 和 Collections 中所有的方法都为静态的,以方便直接传入对象引用,执行相应的功能。http://blog.csdn.net/qq_29663071/article/details/51377334

Java 集合框架体系图



Java 集合框架

  • Collection 接口:存储一组不唯一无序的对象
    • List 接口:   存储一组不唯一有序 (插入顺序)的对象
    • Set 接口:   存储一组唯一(不重复),无序的对象
  • Map 接口:         存储一组键值对象
List 接口存储一组不唯一有序 (插入顺序)的对象:
0 1 2 3 4 5  
aaaa dddd cccc aaaa eeee dddd  
Set 接口存储一组唯一无序的对象
   eeee
       aaaa
dddd         cccc
Map 接口存储一组键值对象,提供 keyvalue 的映射
 -------     --------      -------
|  CHN  |    |  USA  |    |  JPN  |
 -------      -------      -------
| China |    |America|    | Japan |
 -------      -------      -------

ArrayList 实现了长度可变的数组,在内存中分配连续的空间。遍历元素和随机访问元素的效率比较高
0 1 2 3 4 5  
aaaa dddd cccc aaaa eeee dddd  
LinkedList 采用链表存储方式。插入、删除元素时效率比较高

LinkerList 链表式储存方法


ArrayList 集合类

参考:
http://docs.oracle.com/javase/8/docs/api/
http://tool.oschina.net/apidocs/apidoc?api=jdk-zh

实例一:展现 ArrayList 常用方法
/*(1)导入  ArrayList 集合类*/
import java.util.ArrayList;

public class ArrayList_Demo {
    public static void main(String[] args){
        /*(2)创建  ArrayList 对象,并添加数据。
        使用 boolean add(Object o) 方法添加元素(注意:添加到集合中的数据将转换成 Object 类型)*/
        ArrayList list = new ArrayList();
        list.add("张三丰");
        list.add("郭靖");
        list.add("杨过");
        list.add("Fatli");
        /*(3)判断集合中是否包含“李莫愁”。
        使用 boolean contains(Object o) 判断列表中是否存在指定元素*/
        System.out.println(list.contains("李莫愁"));    //输出 false
        /*(4)把索引为 0 的数据移除;把 Fatli 移除。
        使用 Object remove(int index) 从列表中删除指定位置的元素;
        使用 boolean remove(Object o) 从列表中删除元素*/
        list.remove(0);
        list.remove("Fatli");
        /*(5)把索引为 1 的元素替换为"黄蓉"。
        使用 void set(int index,Object obj) 将index 索引位置的元素替换为 obj 元素;*/
        list.set(1,"黄蓉");
        /*(6)输出集合中所有的元素。
        使用 Object get(int index) 返回指定索引位置处的元素(取出的元素是 Object 类型,使用前需要进行强制类型转换)
        还使用 int size() 返回列表中的元素个数;*/
        for(int i = 0;i < list.size();i++){
            String name = (String)list.get(i);
            System.out.print(name+" ");    //输出:郭靖 黄蓉
        }
        /*使用增强 for 循环比普通 for 循环更加简单方便,而且不用考虑下标越界的问题。
        for(Object obj:list){
            String name = (String)obj;
            System.out.println(name);
        }
        */
        /*(7)输出“小龙女”所在的索引位置。
        使用 int indexOf(Object obj) 返回元素在集合中出现的索引位置*/
        System.out.println(list.indexOf("小龙女"));    //因集合中没有该元素,输出: -1
        /*(8)清空 ArrayList 集合中的数据。
        使用 void clear() 移除此列表中的所有元素*/
        list.clear();
        /*判断 ArrayList 集合中是否包含数据。
        使用 boolean isEmpty() 判断 ArrayList 集合是否为空*/
        System.out.println(list.isEmpty());    //输出: true
    }
}

多态:
上面(2)处 ArrayList list = new ArrayList(); 也可以写成 List list = new ArrayList(); 并导入 java.util.List 工具包。
这是将接口 List 的引用指向实现类 ArrayList 的对象,在编程中将接口的引用指向实现类的对象是 Java 实现多态的一种形式,也是软件开发过程中实现低耦合的方式之一。

实例二(实体类+实现类):新闻管理系统:1.可以储存各类新闻标题(包含 ID、名称、创建者);2.可以获取新闻标题总数;3.可以逐条打印新闻标题
package entity;
/**
 * 实体类(同下 LinkedList_Demo 的实体类)
 * @author Fatli
 */
public class NewsTitle {
	private int id;            //ID
	private String titleName;  //名称
	private String creater;    //创建者
	
	public NewsTitle() {	   //无参构造方法
	}
	public NewsTitle(int id, String titleName, String creater) {//有参构造方法(构造方法重载)
		this.id = id;
		this.titleName = titleName;
		this.creater = creater;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getTitleName() {
		return titleName;
	}
	public void setTitleName(String titleName) {
		this.titleName = titleName;
	}
	public String getCreater() {
		return creater;
	}

	public void setCreater(String creater) {
		this.creater = creater;
	}
}
package test;

import java.util.ArrayList;
import java.util.List;

import entity.NewsTitle;

public class ArrayList_Demo2 {
	public static void main(String[] args) {
		// 具体实现步骤
		// 1、创建多个各类新闻标题对象
		NewsTitle politics = new NewsTitle(1, "政治", "管理员");
		NewsTitle economy = new NewsTitle(2, "经济", "管理员");
		// 2、创建存储各类新闻标题的集合对象
		List newsTitleList = new ArrayList();
		// 3、按照顺序依次添加各类新闻标题
		newsTitleList.add(politics);
		newsTitleList.add(economy);
		// 4、获取新闻标题的总数
		System.out.println("新闻标题数目为:" + newsTitleList.size() + "条"); //输出:新闻标题数目为:2条
		// 5、根据位置获取相应新闻标题、逐条打印每条新闻标题的名称,也就是我们常说的遍历集合对象
		for (int i = 0; i < newsTitleList.size(); i++) {
			NewsTitle title = (NewsTitle) newsTitleList.get(i);		
			System.out.print(i + 1 + ":" + title.getTitleName()+" ");	//输出:1:政治 2:经济
		}
	}
}

LinkedList 集合类

LinkedList 类是 List 接口( Collection 接口的子接口)的一个具体实现类
LinkedList 类用于创建链表数据结构
插入或者删除元素时,它提供更好的性能

LinkedList 常用方法:
方法名 说明
void addFirst(Object o) 在列表的首部添加元素
void addLast(Object o) 在列表的末尾添加元素
Object getFirst() 返回列表中的第一个元素
Object getLast() 返回列表中的最后一个元素
Object removeFirst() 删除并返回列表中的第一个元素
Object removeLast() 删除并返回列表中的最后一个元素
实例:增、删、获取头条/末条新闻标题
package entity;
/**
 * 实体类(同上 ArrayList_Demo2 的实体类)
 * @author Fatli
 */
public class NewsTitle {
	private int id;            //ID
	private String titleName;  //名称
	private String creater;    //创建者
	
	public NewsTitle() {	   //无参构造方法
	}
	public NewsTitle(int id, String titleName, String creater) {//有参构造方法(构造方法重载)
		this.id = id;
		this.titleName = titleName;
		this.creater = creater;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getTitleName() {
		return titleName;
	}
	public void setTitleName(String titleName) {
		this.titleName = titleName;
	}
	public String getCreater() {
		return creater;
	}

	public void setCreater(String creater) {
		this.creater = creater;
	}
}
package test;

import java.util.LinkedList;//util工具包
import entity.NewsTitle;	//entity实体包

public class LinkedList_Demo {
	public static void main(String[] args) {
		/*(1)创建多个新闻标题。*/
		NewsTitle politics = new NewsTitle(1,"政治","管理员");
		NewsTitle economy = new NewsTitle(2,"经济","管理员");
		NewsTitle technology = new NewsTitle(3,"科技","管理员");
		NewsTitle entertainment = new NewsTitle(4,"娱乐","管理员");
		/*(2)创建储存新闻标题的集合对象,并添加数据。
		使用 boolean add(Object o) 方法添加元素(注意:添加到集合中的数据将转换成 Object 类型)*/
		LinkedList newsTitleList = new LinkedList();
		newsTitleList.add(politics);	//政治
		newsTitleList.add(economy);		//经济
		/*(3)添加头条新闻标题 和 末条新闻标题。
		使用 void addFirst(Object o) 在列表的首部添加元素 
		使用 void addLast(Object o) 在列表的末尾添加元素 */
		newsTitleList.addFirst(technology);		//科技
		newsTitleList.addLast(entertainment);	//娱乐
		/*(4)获取头条 及 最末条新闻。
		使用 Object getFirst() 返回列表中的第一个元素 
		使用 Object getLast() 返回列表中的最后一个元素 */
		NewsTitle first = (NewsTitle) newsTitleList.getFirst();
		NewsTitle last = (NewsTitle) newsTitleList.getLast();
		/*(5)删除头条和最末条新闻。
		使用 Object removeFirst() 删除并返回列表中的第一个元素 
		使用 Object removeLast() 删除并返回列表中的最后一个元素*/
		newsTitleList.removeFirst();
		newsTitleList.removeLast();
		/*遍历所有新闻标题。使用增强 for 循环。*/
		for(Object obj : newsTitleList){
			NewsTitle nt = (NewsTitle)obj;
			System.out.print(nt.getTitleName()+" ");//输出:政治 经济
		}
	}
}

Set 接口

Set 接口存储一组唯一(不重复),无序的对象
HashSet 是 Set 接口常用的实现类
Set 中存放对象的引用

import java.util.HashSet;
import java.util.Set;

public class SetDemo {
	public static void main(String[] args) {
		Set set=new HashSet();
		String s1=new String("java");
		String s2=s1;
//	  String s3=new String("java");    //输出:1
		String s3=new String("JAVA");    //输出:2
		set.add(s1);
		set.add(s2);
		set.add(s3);
		System.out.println(set.size());
	}
}

HashSet 集合类

HashSet 是 Java 集合框架提供的一个查找效率高的集合类。HashSet 是 Set 接口常用的实现类。

HashSet 集合类的常用方法:
方法 说明
boolean add(Object o) 如果此 Set 中尚未包含指定元素(因为 Set 存储的是唯一不重复的对象),则添加指定元素
void clear() 从此 Set 中移除所有元素
int size() 返回 Set 中的元素数量( Set 的容量)
boolean isEmpty() 如果此 Set 不包含任何元素,则返回 true
boolean contains(Object o) 如果此 Set 包含指定元素,则返回 true
boolean remove(Object o) 如果指定元素存在于此 Set 中,则将其移除
Set newsTitleSet = new HashSet(); 
NewTitle car = new NewTitle(1, "汽车", "管理员"); 
// …… 继续增加元素 
newsTitleSet.add(car); 
// …… 继续获取元素个数 
System.out.println("新闻标题数目为:" + newsTitleList.size() + "条");

Set 接口不存在 get() 方法,错误示范: newsTitleSet.get(0); 思考为什么,以及怎样实现元素输出?——使用 Iterator 迭代器或者 for 循环遍历输出。


附:Java集合框架_hashCode() & equals()



Iterator 迭代器

Iterator 接口表示对集合进行迭代的迭代器。 Iterator 接口为集合而生,专门实现集合的遍历。

Iterator 接口的两个主要方法:

  • boolean hasNext(): 判断是否存在下一个可访问的元素,如果仍有元素可以迭代,则返回 true。
  • Object next(): 返回要访问的下一个元素。

另外,凡是由 Collection 接口派生而来的接口或者实现类,都实现了 iterate() 方法, iterator() 方法返回一个 Iterator 对象。

package entity;
/**
 * 实体类(同上 ArrayListDemo2 、 LinkedList_Demo 一样的实体类)
 * @author Fatli
 */
public class NewsTitle {
	private int id;            //ID
	private String titleName;  //名称
	private String creater;    //创建者
	
	public NewsTitle() {	   //无参构造方法
	}
	public NewsTitle(int id, String titleName, String creater) {//有参构造方法(构造方法重载)
		this.id = id;
		this.titleName = titleName;
		this.creater = creater;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getTitleName() {
		return titleName;
	}
	public void setTitleName(String titleName) {
		this.titleName = titleName;
	}
	public String getCreater() {
		return creater;
	}

	public void setCreater(String creater) {
		this.creater = creater;
	}
}
package test;

import java.util.HashSet;
import java.util.Iterator; //导入 Iterator 接口
import java.util.Set;

import entity.NewsTitle;

public class IteratorDemo {
	public static void main(String[] args) {
		// 1. 创建多个各类新闻标题对象
		NewsTitle politics = new NewsTitle(1, "政治", "管理员");
		NewsTitle economy = new NewsTitle(2, "经济", "管理员");
		// 2. 创建存储各类新闻标题的集合对象
		Set newsTitleList = new HashSet();
		// 3. 按照顺序依次添加各类新闻标题
		newsTitleList.add(politics);
		newsTitleList.add(economy);
		// 4. 获取新闻标题的总数
		System.out.println("新闻标题数目为:" + newsTitleList.size() + "条"); //2
		// 5. 使用 iterator() 方法获取 Iterator 对象
		//凡是由 Collection 接口派生而来的接口或者实现类,都实现了 iterate() 方法, iterator() 方法返回一个 Iterator 对象
		Iterator it = newsTitleList.iterator();
		// 6. 使用 Iterator 遍历集合
		//使用 Iterator 的 hasNext() 方法判断是否存在下一个可访问的元素;使用 Iterator 的 next() 方法返回要访问的下一个元素。
		while(it.hasNext()){
			NewsTitle title = (NewsTitle) it.next();
			System.out.print(title.getTitleName()+" "); //输出:政治 经济(也可能输出:经济 政治。因为 Set 存储的是一组无序的对象 )
		}
		System.out.println("");
		// 7. 方法二:使用增强型 for 遍历集合
		for(Object obj:newsTitleList){
			NewsTitle title = (NewsTitle)obj;
			System.out.print(title.getTitleName()+" "); //输出:政治 经济(也可能输出:经济 政治。因为 Set 存储的是一组无序的对象 )
		}
	}
}


Map 接口

Map 接口专门处理键值映射数据的存储,可以根据键实现对值的操作。
最常用的实现类是 HashMap

Map 接口的常用方法
方法名 说明
Object put(Object key, Object value) 以 “键-值” 对的方式进行存储
Object get(Object key) 根据键返回相关联的值,如果不存在指定的键,返回 null
Object remove(Object key) 删除由指定的键映射的 “键-值对”
int size() 返回元素个数
Set keySet() 返回键的集合
Collection values() 返回值的集合
boolean containsKey(Object key) 如果存在由指定的键映射的 “键-值对”,返回 true
实例:建立国家英文简称和中文全名间的键值映射,并通过 key 对 value 进行操作
package test;

import java.util.HashMap;
import java.util.Map;

public class MapDemo {
	public static void main(String[] args) {
        // 1、使用 HashMap 存储多组国家英文简称和中文全称的键值对
        Map countries = new HashMap();
	    countries.put("CHN", "中华人民共和国");
	    countries.put("USA", "美利坚合众国");
	    countries.put("JPN", "日本国");
        // 2、显示"CHN"对应国家的中文全称
        String country = (String) countries.get("CHN");
        System.out.println("CHN 对应的国家是:" + country);
        // 3、显示集合中元素个数
        System.out.println("Map 中共有"+countries.size()+"组数据"); // 3
        /*4、两次判断 Map 中是否存在 "USA" 键*/
        System.out.println("Map 中包含 USA 的 key 吗?" + 
        countries.containsKey("USA"));    //输出:Map 中包含 USA 的 key 吗?true
        countries.remove("USA");
        System.out.println("Map 中包含 USA 的 key 吗?" + 
        countries.containsKey("USA"));    //输出:Map 中包含 USA 的 key 吗?false
        /* 5、分别显示 键集、值集和键值对集*/
        System.out.println(countries.keySet()); //输出:[CHN, JPN]
        System.out.println(countries.values()); //输出:[中华人民共和国, 日本国]
        System.out.println(countries);			//输出:{CHN=中华人民共和国, JPN=日本国}
        /* 3、清空 HashMap 并判断*/
        countries.clear();
        if(countries.isEmpty())
        	System.out.println("已清空Map中数据!");//输出:已清空Map中数据!
    }
}



泛型

将对象的类型作为参数,指定到其他类或者方法上,从而保证类型转换的安全性和稳定性,这就是泛型。泛型的本质就是参数化类型。

泛型的本质就是参数化类型,也就是说所操作的数据类型都被指定为一个参数,使代码可以应用于多种类型。简单来说, Java 语言引入泛型的好处是安全简单,且所有的强制转换都是自动和隐式的,提高代码的重用率。

泛型的定义语法:

1或者接口<类型实参> 对象 = new2<类型实参>();

实例一:

ArrayList<String> list = new ArrayList<String>();

实例二:

//创建学员集合 
ArrayList<Student> students = new ArrayList<Student>();
//创建学员类对象 
Student student = new Student(); 
//创建诗类对象 
Poem poem = new Poem(); 
//将两个对象添加到list集合中           
students.add(student);  //由于创建时已指定 Student 类型,所以这里会报错 
students.add(poem); 

泛型集合

JDK5.0 使用泛型改写了集合框架中的所有接口和类

泛型在集合中的应用:
例如在 List 接口中的 add() 方法的参数是 Object 类型,在通过 get() 方法取出集合中的元素时必须进行强制类型转换,还可能出现 ClassCastException 异常。另外, Map 接口中使用 put()get() 方法存取对象时,使用 Iterator 接口的 next() 方法取元素时存在同样的问题。
使用泛型集合在创建集合对象时指定集合中元素的类型,从集合中取出时无需进行类型强制转换。如果把非指定类型对象放入集合,会出现编译错误。

List 和 ArrayList 的泛型形式是 List<E>ArrayList<E><E> 表示该泛型集合中的元素类型。

//省略 NewsTitle 实体类。 关键代码:
//使用泛型
List<NewsTitle> newsTitleList = new ArrayList();
//添加集合元素
newsTitleList.add(politics);
newsTitleList.add(economy);
//遍历输出新闻标题
for(NewsTitle nt : newsTitleList){
	System.out.print(nt.getTitleName());
}
/*不再需要类型强制转换:
for(Object obj : newsTitleList){
	NewsTitle nt = (NewsTitle)obj;
	System.out.print(nt.getTitleName()+" ");//输出:政治 经济
}*/

Map 于 HashMap 的泛型形式是 Map<K,V>HashMap<K,V> 。因为它们的每个元素都包含两个部分,即 key 和 value ,所以,在应用泛型时,要同时指定 key 的类型和 value 的类型。 <K,V> 表示该泛型集合中的元素类型,其中, K 表示 key 的类型, V 表示 value 的类型。

package entity;

public class Student {
	private String name; // 学员姓名
	private String sex; // 学员性别
	public Student() {
	}
	public Student(String name, String sex) {
		this.name = name;
		this.sex = sex;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	@Override
	//重写 toString() 方法
	public String toString() {
		return "Student [name=" + name + ", sex=" + sex + "]";
	}
}
package test;

import java.util.HashMap;
import java.util.Map;

import entity.Student;

public class HashMapDemo {
	public static void main(String[] args) {
		// 1.创建学员对象
		Student student1 = new Student("权权", "男");
		Student student2 = new Student("坪坪", "女");
		// 2.创建保存“键-值对”的集合对象
		Map<String,Student> students = new HashMap<String,Student>();
		// 3.把英文名称与学员对象按照“键-值对”的方式存储在 HashMap 中
		students.put("Fatli", student1);
		students.put("Xenia", student2);
		// 4.打印键集
		System.out.println("键集:"+students.keySet());	//键集:[Fatli, Xenia]
		// 5.打印值集
		System.out.println("值集:"+students.values());	//值集:[Student [name=权权, sex=男], Student [name=坪坪, sex=女]] 
														//如果不在实体类 Student 添加(重写)  toString() 方法,输出的是哈希值: [entity.Student@2e7227a8, entity.Student@48899e6a]
		// 6.打印键-值对集合
		System.out.println("键-值对集合:"+students);		//键-值对集合:键-值对集合:{Fatli=Student [name=权权, sex=男], Xenia=Student [name=坪坪, sex=女]}
														//如果不在实体类 Student 添加(重写)  toString() 方法,输出的是哈希值:{Fatli=entity.Student@2e7227a8, Xenia=entity.Student@48899e6a}
		// 7.判断是否存在“Fatli”这个键
		String key = "Fatli";
		if(students.containsKey(key)){
		    // 8.如果存在,根据键获取相应的值
			Student student = students.get(key);
			System.out.println("学员姓名:"+student.getName());	//学员姓名:权权
		}
	}
}

泛型类、泛型接口、泛型方法

在集合中使用泛型只是泛型应用的其中一种,在类、接口、方法中也有着广泛的应用。

泛型类

泛型类,就是具有一个或者多个类型变量的类。

定义泛型类的的语法:

[访问修饰符] class className<TypeList>
泛型接口

泛型接口,就是具有一个或者多个类型变量的接口。

定义泛型接口的的语法:

[访问修饰符] interface interface<TypeList>
泛型方法

泛型方法,实际上就是带有类型参数的方法。

定义泛型方法的语法:

访问修饰符 <类型参数> 返回值 方法名 (类型参数列表)

实例:

public <String> void showName (String s) {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant