黑马程序员————Java基础之集合框架

本文深入讲解Java集合框架的各类集合,包括List、Set、Map等的主要特性与应用场景,对比数组与集合的不同,介绍集合框架的基本操作及迭代器的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

-----------android培训java培训、java学习型技术博客、期待与您交流!------------ 

集合框架

为什么出现集合类?

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

数组与集合类的区别:

数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象(对象的引用地址)。。

集合类的特点:

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

Java中集合类的关系图:

 

注:1. 在集合框架中,接口Map和Collection在层次结构上没有任何亲缘关系,它们是截然不同的。

        2. 不要简单的认为集合类就这些,jdk中集合类有很多这些是我们经常用到的而已,Collection、List、Set、Queue和Map都是接口(Interface),不是具体的类实现。

Collection接口:

Collenction接口是集合框架中的常用接口。其下有两个子接口:List,Set。

所属关系:

       Collenction

                 |--List:元素是有序的,元素可以重复。因为该集合体系有索引。

                 |--Set:元素是无序得,元素不可以重复。

Collection定义了集合框架的共性功能。

1,添加

 add(e);

 addAll(collection);

add方法的参数类型是Object。以便于接收任意类型对象。

2,删除

 remove(e);

 removeAll(collection);

 clear();

3,判断。

 contains(e);

 isEmpty();

4,获取

 iterator();

 size();

5,获取交集。

 retainAll();

6,集合变数组。

 toArray();

迭代器:

什么是迭代器呢?

其实就是集合的取出元素的方式。会直接访问集合中的元素。所以将迭代器通过内部类的形式来进行描述。通过容器的iterator()方法获取该内部类的对象。对于集合的元素取出这个动作:当不足以用一个函数来描述,需要用多个功能来体现,所以就将取出这个动作封装成一个对象来描述。就把取出方式定义在集合的内部,这样取出方式就可以直接访问集合内部的元素。那么取出方式就被定义成了内部类。而每一个容器的数据结构不同,所以取出的动作细节也不一样。但是都具有共性内容: 判断和取出。那么就可以将这些共性抽取。那么这些内部类都符合一个规则(或者说都抽取出来一个规则)。该规则就是Iterator。通过一个对外提供的方法:iterator();,来获取集合的取出对象。因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。

如以下代码就是迭代器的使用方式:

<span style="font-family:SimSun;font-size:12px;">01.Iterator it = collection.iterator();  
02.while(it.hasNext()){  
03.    Object obj = it.next();  
04.}  
</span>

List:

List:元素是有序的,元素可以重复。因为该集合体系有索引。

  |--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。

  |--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。

  |--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。

List的特有方法。凡是可以操作角标的方法都是该体系特有的方法。

 add(index,element);

 addAll(index,Collection);

 remove(index);

 set(index,element);

 get(index):

 subList(from,to);

 listIterator();

 int indexOf(obj):获取指定元素的位置。

 ListIterator listIterator(); 

List集合特有的迭代器。ListIterator是Iterator的子接口。在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。

所以,在迭代器时,只能用迭代器的放过操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作,如果想要其他的操作如添加,修改等,就需

要使用其子接口,ListIterator。该接口只能通过List集合的listIterator方法获取。

listIterator接口特有方法:

add(obj);//增加

set(obj);//修改为obj

hasPrevious();//判断前面有没有元素

previous();//取前一个元素

LinkedList:

底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。

LinkedList:特有方法:

添加元素:

addFirst();

addLast();

获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException

getFirst();

getLast();

获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException

removeFirst();

removeLast();


在JDK1.6出现了替代方法。

添加元素:

offerFirst();

offerLast();

获取元素,但不删除元素。如果集合中没有元素,会返回null。
peekFirst();

peekLast();

获取元素,但是元素被删除。如果集合中没有元素,会返回null。

pollFirst();

pollLast();

Set:

Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。

Set集合的功能和Collection是一致的。

   HashSet:底层数据结构是哈希表。是线程不安全的。不同步。

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

       是通过元素的两个方法,hashCode和equals来完成。

       如果元素的HashCode值相同,才会判断equals是否为true。

       如果元素的hashcode值不同,不会调用equals。

       注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。

       

下面通过一个例子来演示以下:

/*
需求:往hashSet集合中存入自定对象
姓名和年龄相同为同一个人,重复元素。
*/
import java.util.*;
class HashSetTest 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) 
	{
		HashSet hs = new HashSet();

		hs.add(new Person("a1",11));
		hs.add(new Person("a2",12));
		hs.add(new Person("a3",13));	
		hs.add(new Person("a3",13));	

		Iterator it = hs.iterator();

		while(it.hasNext())
		{
			Person p = (Person)it.next();
			sop(p.getName()+"::"+p.getAge());
		}
	}
}
class Person
{
	private String name;
	private int age;
	Person(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	
	public int hashCode()
	{
		
		return name.hashCode()+age*37;
	}

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

	
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
}


 

TreeSet:可以对Set集合中的元素进行排序。

  底层数据结构是二叉树。

  保证元素唯一性的依据:compareTo方法return 0.

  TreeSet排序的第一种方式:让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。 这种方式也称为元素的自然顺序,或者叫做默认顺序。

如以下例子:

import java.util.*;

/*
需求:
往TreeSet集合中存储自定义对象学生。
按照学生的年龄进行排序。
*/

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

		ts.add(new Student("lisi02",22));
		ts.add(new Student("lisi007",20));
		ts.add(new Student("lisi09",19));
		ts.add(new Student("lisi08",19));
	

		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			Student stu = (Student)it.next();
			System.out.println(stu.getName()+"..."+stu.getAge());
		}
	}
}


class Student implements Comparable//该接口强制让学生具备比较性。
{
	private String name;
	private int age;

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

	public int compareTo(Object obj)
	{		
		if(!(obj instanceof Student))
			throw new RuntimeException("类型不匹配");
		Student s = (Student)obj;

		if(this.age>s.age)
			return 1;
		if(this.age==s.age)
		{
			return this.name.compareTo(s.name);
		}
		return -1;

	}

	public String getName()
	{
		return name;

	}
	public int getAge()
	{
		return age;
	}
}

  TreeSet的第二种排序方式。 当元素自身不具备比较性时,或者具备的比较性不是所需要的。 这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。

如以下例子:

/*
需求:
往TreeSet集合中存储自定义对象学生。
按照学生的年龄进行排序。
*/
import java.util.*;
class TreeSetDemo2 
{
	public static void main(String[] args) 
	{
		TreeSet ts = new TreeSet(new MyCompare());

		ts.add(new Student("张三1",22));
		ts.add(new Student("张三4",21));
		ts.add(new Student("张三7",20));
		ts.add(new Student("张三6",19));
		ts.add(new Student("张三2",18));
		ts.add(new Student("张三5",18));
		ts.add(new Student("张三3",29));
		
		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			Student stu = (Student)it.next();
			System.out.println(stu.getName()+"..."+stu.getAge());
		}
	}
}

class Student implements Comparable//该接口强制让学生具备比较性。
{
	private String name;
	private int age;

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

	public int compareTo(Object obj)
	{		
		if(!(obj instanceof Student))
			throw new RuntimeException("类型不匹配");
		Student s = (Student)obj;

		if(this.age>s.age)
			return 1;
		if(this.age==s.age)
		{
			return this.name.compareTo(s.name);
		}
		return -1;

	}

	public String getName()
	{
		return name;

	}
	public int getAge()
	{
		return age;
	}
}

class MyCompare implements Comparator
{
	public int compare(Object o1,Object o2)
	{
		Student s1 = (Student)o1;
		Student s2 = (Student)o2;

		int num = s1.getName().compareTo(s2.getName());
		if(num==0)
		{

			return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
			
		}

		
		return num;

	}
}



 

Map:

Map<K,V>集合是一个接口,和List集合及Set集合不同的是,它是双列集合,并且可以给对象加上名字,即键(Key),该集合存储键值对。一对一对往里存。而且要保证键的唯一性。跟Set很像,Set底层就是使用了Map集合。

Map集合的常用类:

Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。jdk1.0.效率低。

HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。

TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。

Map集合的常用方法:

1,添加。

添加元素,添加元素,如果出现添加时,相同的键。那么后添加的值会覆盖原有键对应值。

  put(K key, V value) 添加元素

  putAll(Map<? extends K,? extends V> m) 添加一个集合

 2,删除。

  clear() 清空集合

  remove(Object key) 删除指点键值

 3,判断。

  containsValue(Object value) 判断值是否存在

  containsKey(Object key) 判断键是否存在

  isEmpty() 判断集合是否为空


 4,获取。

  get(Object key)通过键获取值

  size() 获取集合长度

  values()

5,取出集合元素

  entrySet() 

  keySet()

Map集合元素的两种取出方式:

1,Set<k> keySet:将map中所有的键存入到Set集合。因为set具备迭代器。所有可以迭代方式取出所有的键,在根据get方法。获取每一个键对应的值。 

 Map集合的取出原理:将map集合转成set集合。在通过迭代器取出。如以下代码:

import java.util.*;
class MapDemo 
{
	public static void main(String[] args) 
	{
		Map<String,String> hm = new HashMap<String,String>();
		
		hm.put("02","张三");
		hm.put("01","李四");
		hm.put("04","王五");
		hm.put("03","赵六");
		
		Set<String> keySet = hm.keySet();
		Iterator<String> it = keySet.iterator();
		while (it.hasNext())
		{
			String key=it.next();
			String value=hm.get(key);
			System.out.println(key+":"+value);		
			
		}
		
	}
}

2,Set<Map.Entry<k,v>> entrySet:将map集合中的映射关系存入到了set集合中,而这个关系的数据类型就是:Map.Entry。如以下代码:

import java.util.*;
class MapDemo 
{
	public static void main(String[] args) 
	{
		Map<String,String> hm = new HashMap<String,String>();
		
		hm.put("02","张三");
		hm.put("01","李四");
		hm.put("04","王五");
		hm.put("03","赵六");
		
	
		Set<Map.Entry<String,String>> entrySet = hm.entrySet();
		Iterator<Map.Entry<String,String>> iter = entrySet.iterator();
		while (iter.hasNext())
		{
			Map.Entry<String,String> me=iter.next();
			String key=me.getKey();
			String value=me.getValue();
			System.out.println(key+":"+value);			
		}
	}
}

Entry其实就是Map中的一个static内部接口。为什么要定义在内部呢?因为只有有了Map集合,有了键值对,才会有键值的映射关系。关系属于Map集合中的一个内部事物, 而且该事物在直接访问Map集合中的元素。

什么时候使用Map集合呢?

当数据之间存在这映射关系时,就要先想map集合。如以下例子:

/*
练习:
"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数。
希望打印结果:a(1)c(2).....
通过结果发现,每一个字母都有对应的次数。
说明字母和次数之间都有映射关系。
注意了,当发现有映射关系时,可以选择map集合。因为map集合中存放就是映射关系。
*/
import java.util.*;
class  MapTest3
{
	public static void main(String[] args) 
	{
		String s= charCount("ak+abAf1c,dCkaAbc-defa");
		System.out.println(s);
	}
	
	public static String charCount(String str)
	{
		char[] chs = str.toCharArray();

		TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();

		
		int count = 0;
		for(int x=0; x<chs.length; x++)
		{
			

			if(!(chs[x]>='a' && chs[x]<='z' || chs[x]>='A' && chs[x]<='Z'))
				continue;

			Integer value = tm.get(chs[x]);

			
			if(value!=null)
				count = value;
			count++;
			tm.put(chs[x],count);//直接往集合中存储字符和数字,为什么可以,因为自动装箱。

			count = 0;
		


		}

		StringBuilder sb = new StringBuilder();

		Set<Map.Entry<Character,Integer>> entrySet = tm.entrySet();
		Iterator<Map.Entry<Character,Integer>>  it = entrySet.iterator();

		while(it.hasNext())
		{
			Map.Entry<Character,Integer> me = it.next();
			Character ch = me.getKey();
			Integer value = me.getValue();
			sb.append(ch+"("+value+")");
		}



		return sb.toString();
	}

}


Map集合的扩展:

在现实开发中,比较常见的是一对多的映射关系,这时可以把多个映射存放到Map集合当中,相当于一个大的Map集合里面嵌套了几个小的Map集合。

如以下例子:

import java.util.*;

class Student
{
	private String id;
	private String name;
	Student(String id,String name)
	{
		this.id = id;
		this.name = name;
	}
	public String toString()
	{
		return id+":::"+name;
	}
}
class  MapDemo3
{

	public static void demo()
	{
		HashMap<String,List<Student>> czbk = new HashMap<String,List<Student>>();

		List<Student> reyu = new ArrayList<Student>();
		List<Student> jiuye = new ArrayList<Student>();

		czbk.put("yureban",reyu);
		czbk.put("jiuyeban",jiuye);

		reyu.add(new Student("01","zhagnsa"));
		reyu.add(new Student("04","wangwu"));
		jiuye.add(new Student("01","zhouqi"));
		jiuye.add(new Student("02","zhaoli"));

		Iterator<String> it = czbk.keySet().iterator();

		while(it.hasNext())
		{
			String roomName = it.next();
			List<Student> room = czbk.get(roomName);
			
			System.out.println(roomName);
			getInfos(room);
		}
	
	}
	public static void getInfos(List<Student> list)
	{
		Iterator<Student> it = list.iterator();
		while(it.hasNext())
		{
			Student s = it.next();
			System.out.println(s);
		}
	}

	public static void main(String[] args) 
	{
		 demo();
		
	}
	
}




  
 

 

 

 

 

 

 

 

 

 

 

1. 用户与身体信息管理模块 用户信息管理: 注册登录:支持手机号 / 邮箱注册,密码加密存储,提供第三方快捷登录(模拟) 个人资料:记录基本信息(姓名、年龄、性别、身高、体重、职业) 健康目标:用户设置目标(如 “减重 5kg”“增肌”“维持健康”)及期望周期 身体状态跟踪: 体重记录:定期录入体重数据,生成体重变化曲线(折线图) 身体指标:记录 BMI(自动计算)、体脂率(可选)、基础代谢率(根据身高体重估算) 健康状况:用户可填写特殊情况(如糖尿病、过敏食物、素食偏好),系统据此调整推荐 2. 膳食记录与食物数据库模块 食物数据库: 基础信息:包含常见食物(如米饭、鸡蛋、牛肉)的名称、类别(主食 / 肉类 / 蔬菜等)、每份重量 营养成分:记录每 100g 食物的热量(kcal)、蛋白质、脂肪、碳水化合物、维生素、矿物质含量 数据库维护:管理员可添加新食物、更新营养数据,支持按名称 / 类别检索 膳食记录功能: 快速记录:用户选择食物、输入食用量(克 / 份),系统自动计算摄入的营养成分 餐次分类:按早餐 / 午餐 / 晚餐 / 加餐分类记录,支持上传餐食照片(可选) 批量操作:提供常见套餐模板(如 “三明治 + 牛奶”),一键添加到记录 历史记录:按日期查看过往膳食记录,支持编辑 / 删除错误记录 3. 营养分析模块 每日营养摄入分析: 核心指标计算:统计当日摄入的总热量、蛋白质 / 脂肪 / 碳水化合物占比(按每日推荐量对比) 微量营养素分析:检查维生素(如维生素 C、钙、铁)的摄入是否达标 平衡评估:生成 “营养平衡度” 评分(0-100 分),指出摄入过剩或不足的营养素 趋势分析: 周 / 月营养趋势:用折线图展示近 7 天 / 30 天的热量、三大营养素摄入变化 对比分析:将实际摄入与推荐量对比(如 “蛋白质摄入仅达到推荐量的 70%”) 目标达成率:针对健
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值