一,数据结构简介
常见的数据结构
数据存储的常用结构有:栈、队列、数组、链表和红黑树
。
栈:
栈:stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
特点:
①先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。
②栈的入口、出口的都是栈的顶端位置。
注意:
入栈:也称为压栈,就是存元素。
出栈:也称为弹栈,就是取元素。
队列
队列:queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。
特点:
①先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。
②队列的入口、出口各占一侧。
数组
数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。
特点:
①查找快(通过索引,可以快速访问指定位置的元素)。
②增删慢(指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置。删除同理)。在堆内存中,频繁的创建数组,复制数组中的元素,销毁数组,效率低下。
链表:
链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表。
特点:
①多个结点之间,通过地址进行连接。
②查询慢(链表中的地址并不一定是连续的,所以每次查询需要重头开始)。
③增删快(增加和删除对于链表整体没有影响,只需要修改连接下个元素的地址即可。删除同理)。
链表的结构:
链表中的每一个元素也称之为一个节点,一个节点包含了一个数据源(存储数组),两个指针(存储地址)。
红黑树
二,set接口
`java.util.Set`接口和`java.util.List`接口一样,同样继承自`Collection`接口,
它与`Collection`接口中的方法基本一致,并没有对`Collection`接口进行功能上
的扩充,只是比`Collection`接口更加严格了。与`List`接口不同的是,
`Set`接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
Set接口的特点
①不允许存储重复的元素
②没有索引,没有带索引的方法,也不能使用普通的for循环遍历;可以采用:迭代器、增强for。
HashSet集合介绍
`java.util.HashSet`是`Set`接口的一个实现类,它所存储的元素是
不可重复的,并且元素都是无序的(即存取顺序不一致)。
`java.util.HashSet`底层的实现其实是一个`java.util.HashMap`支持,做了解。
HashSet的特点
①不允许存储重复的元素
②没有索引,没有带索引的方法,也不能使用普通的for循环遍历
③是一个无序的集合,存储元素和取出元素的顺序有可能不一致
④底层是一个哈希表结构(查询的速度非常的快)
注意:HashSet
是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCode
与equals
方法。
//创建一个Set接口
Set<Integer> set = new HashSet<>();
//使用add方法往集合中添加元素
set.add(1);
set.add(3);
set.add(2);
set.add(1);//已经存在的元素,不会被存储
//使用迭代器遍历set集合
Iterator<Integer> it = set.iterator();
while (it.hasNext()){
Integer n = it.next();
System.out.println(n);//1,2,3
}
//使用增强for遍历set集合
System.out.println("-----------------");
for (Integer i : set) {
System.out.println(i);
}
hashCode
①哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到地址,不是数据实际存储的物理地址)
②在Object类有一个方法,可以获取对象的哈希值 int hashCode() 返回该对象的哈希码值。
③hashCode方法:public native int hashCode();native:代表该方法调用的是本地操作系统的方法
LinkedHashSet
在HashSet下面有一个子类`java.util.LinkedHashSet`,它是链表和哈希表组合的一个数据存储结构。
2.5.1 LinkedHashSet集合特点:
底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序。
public class LinkedHashSetDemo {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<String>();
set.add("bbb");
set.add("aaa");
set.add("abc");
set.add("bbc");
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
三,Map接口
①Collection
中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
②Map
中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
③Collection
中的集合称为单列集合,Map
中的集合称为双列集合。
④需要注意的是,Map
中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
Map常用子类
通过查看Map接口描述,看到Map有多个子类,这里我们主要讲解常用的HashMap集合、LinkedHashMap集合。
①HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
②LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
注意:Map接口泛型变量中的集合都有两个<K,V>,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量<K,V>的数据类型可以相同,也可以不同。
Map接口中的常用方法
Map接口中定义了很多方法,常用的如下:
① public V put(K key, V value)
: 把指定的键与指定的值添加到Map集合中。
② public V remove(Object key)
: 把指定的键所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
③ public V get(Object key)
:根据指定的键,在Map集合中获取对应的值。
④ boolean containsKey(Object key)
:判断集合中是否包含指定的键。
⑤ public Set<K> keySet()
: 获取Map集合中所有的键,存储到Set集合中。
⑥ `public Set<Map.Entry<K,V>> 获取到Map集合中所有的键值对对象的集合(Set集合)。
注意:
使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,
返回null,并把指定的键值添加到集合中; 若指定的键(key)在集合中存
在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。
Map集合遍历键找值方式
键找值方式:即通过元素中的键,获取键所对应的值
步骤:
1. 获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。
2. 遍历键的Set集合,得到每一个键。
3. 根据键,获取键所对应的值。
//获取所有的键
Set<String> keys = map.keySet();
// 遍历键集 得到每一个键
for (String key : keys) {
//通过键获取对应值
String value = map.get(key);
System.out.println(key+"的CP是:"+value);
}
1.6 Map集合遍历键值对方式
键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值
操作步骤:
①获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。
②遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象。
③通过键值对(Entry)对象,获取Entry对象中的键与值。
// 创建HashMap集合对象
HashMap<String, String> map = new HashMap<String,String>();
// 添加元素到集合
map.put("胡歌", "霍建华");
map.put("郭德纲", "于谦");
map.put("薛之谦", "大张伟");
// 获取 所有的 entry对象 entrySet
Set<Map.Entry<String,String>> entrySet = map.entrySet();
// 遍历得到每一个entry对象
for (Map.Entry<String, String> entry : entrySet) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"的CP是:"+value);
}
注意:
Map集合不能直接使用迭代器或者foreach进行遍历。但是转成Set之后就可以使用了。
1.7 HashMap存储自定义类型键值
练习:每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到 map集合中。学生作为键, 家庭住址作为值。(注意,学生姓名相同并且年龄相同视为同一名学生。)
①当给HashMap中存放自定义对象时,如果自定义对象作为key存在,这时要保证对象唯一,必须复写对象的 hashCode和equals方法。
②如果要保证map中存放的key和取出的顺序一致,可以使java.util.LinkedHashMap 集合来存放。
LinkedHashMap
我们知道HashMap保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,还要速度快怎么办呢? 在HashMap下面有一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。
代码实例:
public static void main(String[] args) {
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
map.put("邓超", "孙俪");
map.put("李晨", "范冰冰");
map.put("刘德华", "朱丽倩");
Set<Entry<String, String>> entrySet = map.entrySet();
for (Entry<String, String> entry : entrySet) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
}