Java面向对象(九):集合

集合

简介
• 之前我们学习过数组,数组是可以保存多个数据(对象)的一种结构,但是数组有一定的局限性。
– 数组在内存存储方面的特点:
• 数组初始化以后,长度就确定了。
• 数组声明的类型,就决定了进行元素初始化时的类型
– 数组在存储数据方面的弊端:
• 数组初始化以后,长度就不可变了,不便于扩展
• 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。同时无法直接获取存储元素的个数
• 数组存储的数据是有序的、可以重复的。-
• 存储数据的特点单一。
• Java集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组。

什么是集合?
集合也称容器,是装载一组对象的容器。例如:客户列表、订单列表
学习集合框架的总体思路
• 如何添加元素
• 如何获得元素
• 如何删除元素
• 如何遍历元素

集合框架图示

在这里插入图片描述
在这里插入图片描述

• 集合中的常用接口
– Collection接口
• Collection接口定义标准
– List
• 继承 Collection接口,存储的数据对象有序可重复
– Set
• 继承 Collection接口,存储的数据对象无序不重复
– Map
• 是一组成对的键-值对象,即所持有的是key-value 对。Map中不能有重复的key

Collection 接口

在这里插入图片描述

Iterator 接口

 Iterator对象称为迭代器(设计模式的一种),主要用于遍历Collection集合中的元素。

 迭代器模式定义为提供一种方法访问一个容器(container)对象中各个元索,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生的。类似于“公交车上的售票员”、“火车上的乘务员”、“空姐”。

 Collection接口继承了java.lang.lterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。

 Iterator仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建iterator对象,则必须有一个被迭代的集合。

 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
在这里插入图片描述

Iterator 接口

 boolean hasNext()
 Object next()
 void remove()
注意
Iterator本身不是集合,不会生成集合的副本,迭代器遍历的就是集合本身。
在迭代器迭代元素的时候,想删除元素,需要使用迭代器的remove()方法,不能使用Collection自带的remove()方法。

增强for,内部依然使用的是迭代器实现的。(想深入探究是否真的是迭代器的可以自己把编译了的.class文件进行反编译,再查看源码,就会发现增强for是使用了迭代器实现的)
 增强for的小练习:下面代码输出结果是什么?
在这里插入图片描述

运行结果:
哈哈
哈哈
哈哈

 增强for的练习:
在这里插入图片描述

运行结果:
呵呵
呵呵
呵呵

重写equals、hashCode方法
如添加在集合中的元素为自己写的类的对象,想通过自定义的属性来区别是否为同一个元素时,我们需要重写equals、hashCode方法,重写方法可以直接使用编辑器的自动生成。(不理解的可以点击这里

List 接口

– 特性
• 继承 Collection,允许重复,以元素安插的次序来放置元素,不会重新排列
 三个主要的类:ArrayList、LinkedList、Vector的对比,
 ArrayList底层是数组结构,查询快,增删慢,线程不安全,效率高。
 LinkedList底层是链表数据结构,查询慢,增删快,线程不安全,效率高。
 Vector底层是数组结构,查询快,增删慢,线程安全,效率低。
在这里插入图片描述
分析:
ArrayList 数组实现,(存放地址是连续的)实现List接口,实际上是一个动态可变数组实现(Object [] E),可以扩容,扩容的原则是增加50%
–JDK1.7前,初始化的时候直接创建了10个元素的数组,
–JDK1.8后,初始化的时候创建的是空数组,当第一次保存数据的时候,创建10个元素的数组
实际的开发中,如果能够大致知道元素多少,可以通过构造器:public ArrayList(int initialCapacity)直接给一个初始值,可以减少扩容次数,提高效率。

linkedList链接列表实现(也就是链表,还是双向链表,存放地址是不连续的)

Vector,是一个可再分配的Object(动态)数组
和ArrayList一样,只是方法有synchronized关键字,都是同步方法,线程安全的。
ArrayList是线程不安全的,在实际开发中,推荐使用线程不安全,执行效率高;线程的安全问题应该交给应用程序来控制,而不是有工具类完成,Vector在开发中比较少用。

Collections类

• 类java.util.Collections 提供了一些静态方法实现了基于List容器的一些常用算法
– void sort(List) 对List容器内的元素排序
– void shuffle(List) 随机排序
– void reverse(List) 反转
– void fill(List,Object) 用特定对象重写整个List容器

@Test
	public void testCollections1() {
		List list=new ArrayList();
		
		list.add("D");
		list.add("F");
		list.add("A");
		list.add("J");
		list.add("B");
		
		//排序
		Collections.sort(list);
		System.out.println(list);
		
		//反转
		Collections.reverse(list);
		System.out.println(list);
		
		//随机排序
		Collections.shuffle(list);
		System.out.println(list);
		//填充
		Collections.fill(list, "000");
		System.out.println(list);
	}

Comparable接口

• ?前面的算法根据什么确定对象“大小”
• 所有可以排序的类都实现了java.lang.Comparable接口,这个接口只有一个方法:
– public int compareTo(Object obj)
• 如果需要比较自己创建的对象时,需要给这个类定义一个排序规则,需要实现Comparable接口,重写compareTo()方法,定义排序规则。
• 如:
public int compareTo(Object o){
Person p = (Person)o;
return pname.compareTo(p.pname);
}

List面试题
• 面试题:输出什么内容?
在这里插入图片描述

运行结果:
1
2

Set 接口

Set接口特性
– 继承 Collection,但不允许重复,无序的。
– Set接口没有引入新方法,所以Set就是一个Collection,只不过其行为不同。

HashSet:作为Set接口的主要实现类;线程不安全的,可以存储null值。
• HashSet底层:数组+链表的结构。
底层也是数组,初始容量为16,当如果使用率超过0.75,(16*0.75=12)就会扩大容量为原来的2倍。(16扩容为32,依次为64、128、…等)

LinkedHashSet: 作为HashSet的子类; 遍历其内部数据时,可以按照添加的顺序遍历。可以保证怎么存就怎么取。

TreeSet: 可以按照添加对象的指定属性,进行排序。

 Set接口添加元素的过程:以HashSet 为例:我们向HashSet中添加元素a,
 首先调用元素a的hashCode()方法来计算元素a的哈希值,此哈希值通过某种算法再计算出元素应该在HashSet底层数组中的存放位置(即为:索引位置) ,判断数组此位置上是否已经有元素:
– 如果此位置上没有其他元素,则元素a添加成功。
– 如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
• 如果hash值不相同,则元素a添加成功(以链表方式添加到这个位置上)。
• 如果hash值相同,进而需要调用元素a所在类的equals()方法
– equals().返回true,元素a添加失败
– equals()返回false,则元素a添加成功。

交集并集

		Set s1 = new HashSet();
		s1.add("a");s1.add("b");s1.add("c");
		Set s2 = new HashSet();
		s2.add("a");s2.add("b");s2.add("f");
		Set sn = new HashSet(s1);
		//并集
		sn.addAll(s2);
		System.out.println(sn);
		Set su = new HashSet(s1);
		//交集
		su.retainAll(s2);
		System.out.println(su);

TreeSet类:

 要求添加的元素必须是相同的类对象。也就是说不能添加不同类的对象的元素。并且对象是可以比较的。

 TreeSet的元素支持2种排序方式:自然排序或者根据提供的Comparator进行排序。

 还要特别注意一点:TreeSet判断两个对象是否相等,不是根据equals方法判断的;它是根据compareTo方法判断的,也就是说两个对象compareTo返回0,说明两个对象相等,就不会添加第二个对象了。(不会重复添加相同对象)

 TreeSet排序方式:自然排序

@Test
	public void testHashSet() {
		Set set1=new HashSet();
	
		set1.add("g");
		set1.add("d");
		set1.add("a");
		set1.add("f");
		
		System.out.println(set1);// [a, d, f, g]
	}

 TreeSet排序方式:定制排序。
在这里插入图片描述
在这里插入图片描述

Set面试题1
如何去除List集合中的重复元素?
在这里插入图片描述
Set面试题2
输出什么内容?
在这里插入图片描述

运行结果:
[Person [id=1002, name=BB], Person [id=1001, name=AA]]
[Person [id=1002, name=BB], Person [id=1001, name=CC]]
[Person [id=1002, name=BB], Person [id=1001, name=CC], Person [id=1001, name=CC]]
[Person [id=1002, name=BB], Person [id=1001, name=CC], Person [id=1001, name=CC], Person [id=1001, name=AA]]

Map 接口

Map接口特征
– Map提供了一种映射关系,其中的元素是以键值对(key-value)的形式存储的,能够实现根据key快速查找value。
– Map中的键值对以Entry类型的对象实例形式存在。
– 键(key值)不可重复,value值可以重复,一个value值可以和很多key值形成对应关系,每个键最多只能映射到一个值。
– Map支持泛型,形式如:Map<K,V>。
– Map中使用put(K key,V value)方法添加.

Map接口类图结构
在这里插入图片描述
– 主要方法
在这里插入图片描述
• Map主要实现类
在这里插入图片描述
在这里插入图片描述
HashMap:是Map的主要实现类,开发中通常用这个类。线程不安全的,效率高。可以存储null值(key-value都可以为null)。

LinkedHashMap是HashMap的子类,使用链表方式保证元素的有序性,就是怎么存怎么取。原理是在原有的HashMap上添加了一对指针,保存上一个元素和下一个元素的指针。在频繁遍历元素的时候,效率比较高,可以考虑使用这个类。

Hashtable:古老的实现类,线程安全的,效率低。(不能存储null值,key和value都不能是null)

Properties类,是Hashtable类的子类,常用来处理配置文件。key和value都是String类型

TreeMap:保证按照添加的key-value进行排序, 实现排序遍历。此时考虑key的自然排序或定制排序。

HashMap的底层:
• 数组+链表(jdk7及之前)
• 数组+链表+红黑树(jdk 8)

• Map的底层存储说明:
Map结构的理解:
– Map中的key:无序的、不可重复的,使用set存储所有的key。
– Map中的value:无序的、可重复的,使用Collection 存储所有的value。
– 一个键值对key-value构成了一个Entry对象。
– Map中的Entry:无序的、不可重复的,使用Set存储所有的Entry。

 Map的底层存储过程说明:
– HashMap的底层实现原理:(以jdk7为例说明)
– HashMap map = new HashMap():在实例化以后,底层创建了长度是16的数组Entry[] table。当调用map类的put(key1, value1)时:
– 首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。如果此位置上的数据为空,此时的key1-value1添加成功。
– 如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1 -value1添加成功。
– 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)如果equals()返回false:此时key1-vaLue1添加成功。
– 如果equals()返回true:使用value1替换vaLue2。
– 默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。

 jdk8相较于jdk7在底层实现方面的不同:
 1.new HashMap():底层没有创建一个长度为16的数组。
 2.jdk8底层的数组是: Node[], 而非Entry[]。
 3.首次调用put()方法时,底层创建长度为16的数组。
 4.jdk7底层结构只有:数组+链表。
jdk8中底层结构:数组+链表+红黑树。
Map演示

		Map m = new HashMap();
		m.put("1001",new Person("333","张三"));
		m.put("1002",new Person("444","李四"));
		m.put("1003",new Person("555","王五"));
		//System.out.println(m.get("1002"));
		//m.put("1001",new Person("200333","张三"));
		Set s = m.keySet();
		
		Iterator it = s.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}

 LinkedHashMap的演示:有序的演示

在这里插入图片描述
 TreeMap的演示:自然排序方式演示

在这里插入图片描述
 TreeMap的演示:定制排序方式演示
在这里插入图片描述
• Properties类的演示
在这里插入图片描述

总结

数据结构
ArrayXxx:底层数据结构是数组,查询快,增删慢
LinkedXxx:底层数据结构是链表,查询慢,增删快
HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()
TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序

buibuib…

继续学习…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你不懂、、、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值