1. 集合体系
在Java集合中,分为两大类集合派系Map集合(双列集合)和Collection集合(单列集合)
1. 单列集合
Collection接口是所有单列集合的顶层父类,所有的单列集合都直接或间接实现Collection接口,在Collection接口中,定义了一些的抽象方法为所有的单列集合之规范
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()
: 把集合中的元素,存储到数组中。
以上这些方法都是所有单列集合所遵循的规则
/ 创建集合对象,使用多态形式
Collection<String> coll = new ArrayList<String>();
// 使用方法
/ 添加功能 boolean add(String s)
coll.add("小李广");
coll.add("扫地僧");
coll.add("石破天");
System.out.println(coll);
/ boolean contains(E e) 判断o是否在集合中存在
System.out.println("判断 扫地僧 是否在集合中"+coll.contains("扫地僧"));
/boolean remove(E e) 删除在集合中的o元素
System.out.println("删除石破天:"+coll.remove("石破天"));
System.out.println("操作之后集合中元素:"+coll);
/ size() 集合中有几个元素
System.out.println("集合中有"+coll.size()+"个元素");
/ Object[] toArray()转换成一个Object数组
Object[] objects = coll.toArray();
/ 遍历数组
for (int i = 0; i < objects.length; i++) {
System.out.println(objects[i]);
}
/ void clear() 清空集合
coll.clear();
System.out.println("集合中内容为:"+coll);
/ boolean isEmpty() 判断是否为空
System.out.println(coll.isEmpty());
2. 双列集合
Map集合是所有双列集合的顶层父类,在Map集合中没一个元素都是一个键值对的形式存储。
什么是键值对?如下图
身份证号与姓名,函数中的X与Y等,前一个变量不能重复(身份证号,函数中的X),后一个变量可以重复(姓名,Y),类似的这样的形式称为键值对。(K=X,V=Y)
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集合)。
/创建 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("黄晓明"));
System.out.println(map.get("邓超"));
使用keySet方法变量双列集合
//创建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);
}
Entry键值对对象
在Map体系集合中,其内容是由一对对的键值对构成的,我们将这些键值对,向上抽取出来,找到其共同特点,定义一个对象,称为Entry键值对对象
具体方法
public Set<Map.Entry<K,V>> entrySet()
: 得到一个Set集合,该集合是单列集合,存储的是一个个键值对对象。
方法展示
/ 创建Map集合对象
HashMap<String, String> map = new HashMap<String,String>();
/ 添加元素到集合
map.put("胡歌", "霍建华");
map.put("郭德纲", "于谦");
map.put("薛之谦", "大张伟");
/获取键值对的Set集合
Set<Map.Entry<String, String>> entries = map.entrySet();
/使用迭代器获取迭代对象
Iterator<Map.Entry<String, String>> iterator = entries.iterator();
/遍历集合
while (iterator.hasNext()){
/获取一个一个键值对对象
Map.Entry<String, String> next = iterator.next();
System.out.println(next.getKey()+","+next.getValue());
}
在遍历上面集合中,使用了迭代器对象,其实迭代器就像一个张表,从表头开始。
2. List集合
List 接口继承自Collection接口,是单列集合的一个重要分支
- 在List集合中允许出现重复的元素
- List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致
1. 常用方法
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
这些方法和Collection接口一样,只是在其基础功能上实现增强
2. List的子类
ArrayList集合
ArrayList
集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList
是最常用的集合
在之前文章中的常用类中,也提到该类都使用方法
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。
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> link = new LinkedList<String>();
/添加元素
link.addFirst("abc1");
link.addFirst("abc2");
link.addFirst("abc3");
System.out.println(link);
/ 获取元素
System.out.println(link.getFirst());
System.out.println(link.getLast());
/ 删除元素
System.out.println(link.removeFirst());
System.out.println(link.removeLast());
while (!link.isEmpty()) { /判断集合是否为空
System.out.println(link.pop()); /弹出集合中的栈顶元素
}
System.out.println(link);
}
}
3. Set接口
Set
接口和List
接口一样,同样继承自Collection
接口,Set
并没有对Collection
接口进行功能上的扩充,只是比Collection
接口更加严格了
- Set接口中元素无序
- 存入的元素不出现重复
1. HashSet集合
HashSet
是Set
接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。HashSet
底层的实现其实是一个HashMap
支持。
HashSet
是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCode
与equals
方法。
不重复存储原理
- 在HashSet集合添加元素时,首先调用HashCode方法计算该元素的Hash值(类似于地址值)
- 在从集合中查找是否有相同的Hash值,若没有,则添加
- 若有,则调用equals方法,比较内容是否相同(这里要注意,equals方法本身比较的就是Hash值,使用要覆写元素的equals方法),相同不存储,不相同存储
原理图
- 创建被添加的元素Peson类
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//覆写equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
//覆写toString方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 测试代码
/ 创建集合对象
HashSet<Person> set = new HashSet<>();
/ 添加三个相同元素
set.add(new Person("xxx",2));
set.add(new Person("xxx",2));
set.add(new Person("xxx",2));
System.out.println(set); /显示结果只有一个 [Person{name='xxx', age=2}]
LinkedHashSet集合
我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?
在HashSet下面有一个子类LinkedHashSet ,它是链表和哈希表组合的一个数据存储结构。可以使元素有顺序的存储
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()); // 有序输出
}
结果为
4. Collections集合工具类
1. 常用方法
public static <T> boolean addAll(Collection<T> c, T... elements)
:往集合中添加一些元素。public static void shuffle(List<?> list) 打乱顺序
:打乱集合顺序。public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序。public static <T> void sort(List<T> list,Comparator<? super T> )
:将集合中元素按照指定规则排序。
addAll方法与排序
ArrayList<Integer> list = new ArrayList<Integer>();
/原来写法
// list.add(12);
// list.add(14);
// list.add(15);
// list.add(1000);
// 采用工具类往集合中添加元素
Collections.addAll(list, 5, 222, 1, 2);
System.out.println(list); /[5, 222, 1, 2]
/默认排序方法
Collections.sort(list);
System.out.println(list); /[1, 2, 5, 222]
/打乱排序
Collections.shuffle(list);
System.out.println(list); /[1, 5, 2, 222]
sort方法之指定排序
比较器
java.lang.Comparable
接口:该接口主要是在定义类是,直接提前确定好排序规则,相对来说比较固定java.util.Comparator
接口: 该接口主要是在创建对象之后来,在具体排序时确定排序规则,相对来说比较灵活
我们都知道,在Java中的String类,是有固定的排序规则的,查看源码发现,String实现了java.lang.Comparable
接口,源码如下,但是我们没有权限改变源码,所以可以采用第二中方式,曲线救国改变排序方式
String部分源码
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {......}
Comparator接口
该接口中有一个比较的方法
public int compare(String o1, String o2)
:比较其两个参数的顺序。
两个对象比较的结果有三种:大于,等于,小于。
如果要按照升序排序,由小到大
则o1 小于o2,返回(负数),相等返回0,01大于02返回(正数)
如果要按照降序排序,由大到小
则o1 小于o2,返回(正数),相等返回0,01大于02返回(负数)
ArrayList<String> list = new ArrayList<String>();
list.add("cba");
list.add("aba");
list.add("sba");
list.add("nba");
/排序方法 按照第一个单词的降序,
/注意这里使用了匿名内部类的方式
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.charAt(0) - o1.charAt(0);
}
});
System.out.println(list);
如上图所示,从new
开始,一直到}
,将其看成是一个实现Comparator的一个没有名字的类
不使用匿名内部类的方式
- 首先定义一个类去实现Comparator接口,如下
class xx implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
return o2.charAt(0) - o1.charAt(0);
}
}
- 然后使用该类
ArrayList<String> list = new ArrayList<String>();
list.add("cba");
list.add("aba");
list.add("sba");
list.add("nba");
/匿名对象的方式,使用XX类排序模式
Collections.sort(list,new xx());
System.out.println(list);
5. Java中数据结构
1.栈
- 栈:stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
简单的说:采用该结构的集合,对元素的存取有如下的特点
-
先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。
-
栈的入口、出口的都是栈的顶端位置。
这里两个名词需要注意: -
压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
-
弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。
2.队列
- 队列:queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
- 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。
- 队列的入口、出口各占一侧。例如,下图中的左侧为入口,右侧为出口。
3.数组
- 数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
-
查找元素快:通过索引,可以快速访问指定位置的元素
-
增删元素慢
4.链表
- 链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时i动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
-
多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。
-
查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素
-
增删元素快:
-
增加元素:只需要修改连接下个元素的地址即可。
-
删除元素:只需要修改连接下个元素的地址即可。
-
5.红黑树
- 二叉树:binary tree ,是每个结点不超过2的有序树(tree) 。
简单的理解,就是一种类似于我们生活中树的结构,只不过每个结点上都最多只能有两个子结点。
二叉树是每个节点最多有两个子树的树结构。顶上的叫根结点,两边被称作“左子树”和“右子树”。
如图:
我们要说的是二叉树的一种比较有意思的叫做红黑树,红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。
红黑树的约束:
- 节点可以是红色的或者黑色的
- 根节点是黑色的
- 叶子节点(特指空节点)是黑色的
- 每个红色节点的子节点都是黑色的
- 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同
红黑树的特点:
速度特别快,趋近平衡树,查找叶子元素最少和最多次数不多于二倍