目录
一. Java中的数据结构
1.1 常见的数据结构
数据存储的常用结构有:栈、队列、数组、链表和红黑树。我们分别来了解一下:
1.1.1 栈
栈(stack):又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
简单的说:采用该结构的集合,对元素的存取有如下的特点:
-
先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。
-
栈的入口、出口的都是栈的顶端位置。
这里两个名词需要注意:
-
压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
-
弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。
1.1.2 队列
队列(queue):简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
-
先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。
-
队列的入口、出口各占一侧。例如,下图中的左侧为入口,右侧为出口。
1.1.3 数组
数组(Array):是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
-
查找元素快:通过索引,可以快速访问指定位置的元素
-
增删元素慢
-
指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置。如下图
-
指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中。如下图
-
1.1.4 链表
链表(linkedList):由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时i动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。常说的链表结构有单向链表与双向链表,这里介绍的是单向链表。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
-
多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。
-
查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素
-
增删元素快:
-
增加元素:只需要修改连接下个元素的地址即可。
-
删除元素:只需要修改连接下个元素的地址即可。
-
1.1.5 红黑树
二叉树(binary tree):是每个结点不超过2的有序树(tree) 。
简单的理解,就是一种类似于我们生活中树的结构,只不过每个结点上都最多只能有两个子结点。二叉树是每个节点最多有两个子树的树结构。顶上的叫根结点,两边被称作“左子树”和“右子树”。
如图:
我们要说的是二叉树的一种比较有意思的叫做红黑树,红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。
红黑树的约束:
- 节点可以是红色的或者黑色的
- 根节点是黑色的
- 叶子节点(特指空节点)是黑色的
- 每个红色节点的子节点都是黑色的
- 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同
红黑树的特点:
速度特别快,趋近平衡树,查找叶子元素最少和最多次数不多于二倍
二. Collection集合
2.1 collection概述
Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,有两个重要的子接口,分别是 java.util.List 和 java.util.Set。
Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。方法如下:
- public boolean add(E e) : 把给定的对象添加到当前集合中 。
- public void clear() :清空集合中所有的元素。
- public boolean remove(E e) : 把给定的对象在当前集合中删除。
- public boolean contains(E e) : 判断当前集合中是否包含给定的对象。
- public boolean isEmpty() : 判断当前集合是否为空。
- public int size() : 返回集合中元素的个数。
- public Object[] toArray() : 把集合中的元素,存储到数组中。
2.2 List接口
2.2.1 List集合概述
java.util.List 接口继承自 Collection 接口,是单列集合的一个重要分支,习惯性地会将实现了List接口的对象称为List集合。在List集合中允许出现重复的元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引来访问集合中的指定元素。另外,List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致。
List接口特点:
- 1. 它是一个元素存取有序的集合。
- 2. 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素。
- 3. 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法,如下:
- public void add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上。
- public E get(int index) :返回集合中指定位置的元素。
- public E remove(int index) : 移除列表中指定位置的元素, 返回的是被移除的元素。
- public E set(int index, E element) :用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
2.2.2 ArrayList集合
java.util.ArrayList 集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以 ArrayList 是最常用的集合。
常用的方法有:
- public boolean add(E e) :将指定的元素添加到此集合的尾部。
- public E remove(int index) :移除此集合中指定位置上的元素。返回被删除的元素。
- public E get(int index) :返回此集合中指定位置上的元素。返回获取的元素。
- public int size() :返回此集合中的元素数。遍历集合时,可以控制索引范围,防止越界。
2.2.3 LinkedList集合
java.util.LinkedList:集合数据存储的结构是链表结构。方便元素添加、删除的集合。LinkedList是一个双向链表,图示如下:
常用的方法有:
- public void addFirst(E e) :将指定元素插入此列表的开头。
- public void addLast(E e) :将指定元素添加到此列表的结尾。
- public E getFirst() :返回此列表的第一个元素。
- public E getLast() :返回此列表的最后一个元素。
- public E removeFirst() :移除并返回此列表的第一个元素。
- public E removeLast() :移除并返回此列表的最后一个元素。
- public E pop() :从此列表所表示的堆栈处弹出一个元素。
- public void push(E e) :将元素推入此列表所表示的堆栈。
- public boolean isEmpty() :如果列表不包含元素,则返回true。
2.3 Set接口
java.util.Set:同样继承自 Collection 接口,与 Collection 接口中的方法基本一致。Set 接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
2.3.1 HashSet集合
1. java.util.HashSet :是 Set 接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。底层的实现其实是一个`java.util.HashMap`支持。HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCode与equals方法。
2. 关于哈希表:
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。
哈希存储流程图:
总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
2.3.2 LinkedHashSet
在HashSet下面有一个子类java.util.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());
}
}
}
结果:
bbb
aaa
abc
bbc
三. Map查找表
Map体现的结构是一个多行两列的表格,其中左列称为key,右列称为value。
- Map总是成对保存数据,并且总是根据key获取对应的value,因此我们可以将查询的条件作为key查询对应的结果作为value保存到Map中。
- Map有一个要求:key不允许重复(equals比较的结果)
3.1 常用实现类
-
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
-
LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
3.2 常用方法
- 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>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。
public class MapDemo {
public static void main(String[] args) {
//创建 map对象
HashMap<String, String> map = new HashMap<String, String>();
//添加元素到集合
map.put("西游记", "孙悟空");
map.put("红楼梦", "贾宝玉");
map.put("三国演义", "曹孟德");
System.out.println(map);
//String remove(String key)
System.out.println(map.remove("西游记"));
System.out.println(map);
// 想要查看 红楼梦里有谁
System.out.println(map.get("红楼梦"));
}
}
注意:
- 使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中;
- 若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。
3.3 Map集合遍历
3.3.1 遍历所有的key
键找值方式:即通过元素中的键,获取键所对应的值。
分析步骤:
- 1. 获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法提示:keyset()
- 2. 遍历键的Set集合,得到每一个键。
- 3. 根据键,获取键所对应的值。方法提示:get(K key)
public class MapDemo01 {
public static void main(String[] args) {
//创建Map集合对象
HashMap<String, String> map = new HashMap<String, String>();
//添加元素到集合
map.put("郭德纲", "于谦");
map.put("孟鹤堂", "周九良");
map.put("郭麒麟","闫壮壮");
//获取所有的键 获取键集
Set<String> keys = map.keySet();
// 遍历键集 得到 每一个键
for (String key : keys) {
//key 就是键
//获取对应值
String value = map.get(key);
System.out.println(key + "的CP是:" + value);
}
}
}
3.3.2 Entry键值对对象
键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。
操作步骤:
- 获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。方法提示:entrySet()。
- 遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象。
- 通过键值对(Entry)对象,获取Entry对象中的键与值。 方法提示:getkey() getValue()。
public class MapDemo02 { public static void main(String[] args) { // 创建Map集合对象 HashMap<String, String> map = new HashMap<String, String>(); // 添加元素到集合 map.put("郭德纲", "于谦"); map.put("孟鹤堂", "周九良"); map.put("郭麒麟","闫壮壮"); // 获取 所有的 entry对象 entrySet Set<Entry<String, String>> entrySet = map.entrySet(); // 遍历得到每一个entry对象 for (Entry<String, String> entry : entrySet) { // 解析 String key = entry.getKey(); String value = entry.getValue(); System.out.println(key + "的CP是:" + value); } } }
3.3.3 遍历所有的Value
此处用到Collection values(),该方法会将当前Map中所有的value以一个集合形式返回。
public class MapDemo02 {
public static void main(String[] args) {
// 创建Map集合对象
HashMap<String, String> map = new HashMap<String, String>();
// 添加元素到集合
map.put("郭德纲", "于谦");
map.put("孟鹤堂", "周九良");
map.put("郭麒麟", "闫壮壮");
Collection<String> values = map.values();
for(String value : values){
System.out.println("value:" + value);
}
}
}
3.3.4 Lambda表达式遍历
JDK8之后集合框架支持了使用lambda表达式遍历。因此Map和Collection都提供了foreach方法,通过lambda表达式遍历元素。
public class MapDemo02 {
public static void main(String[] args) {
// 创建Map集合对象
HashMap<String, String> map = new HashMap<String, String>();
// 添加元素到集合
map.put("郭德纲", "于谦");
map.put("孟鹤堂", "周九良");
map.put("郭麒麟", "闫壮壮");
map.forEach(
(k, v) -> System.out.println(k + ":" + v)
);
}
}
3.4 LinkedHashMap
LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。来保证元素有序,速度快。
public class LinkedHashMapDemo {
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());
}
}
}
执行结果:
西游记 猪八戒
红楼梦 林黛玉
水浒传 武大郎
最后,附一张图供各位参考
本文完!
写在结尾:
一个java菜鸟,发布于北京海淀。
好记性不如烂笔头,持续学习,坚持输出~今天是持续写作的第11天。可以点赞、评论、收藏啦。