1.数据结构
1.1. 常见的数据结构
数据储存常用结构有:栈、队列、数组、链表、红黑树
栈:
栈:Stack ,又称堆栈,它是运算受限的线性表,它的限制是仅仅允许在表的一端进行插入和删除操作,不允许在表的其他位置进行添加、查找、删除等操作。采取栈结构的集合,对元素的存取有以下特点。
(1) 先进后出 :先存进去的元素,要等到后存进去的元素取出来才能取出。
(2)栈的入口、出口都在栈的顶端位置。
压栈:就是存元素。就是把元素存储到栈的顶端位置,栈中的已有元素依次向栈底方向移动一个位置。
弹栈:就是取元素。就是把栈顶端位置的元素取出来,栈中的已有元素依次向栈顶方向移动一个位置。
队列:
队列:queue,简称队列,它与堆栈一样,也是运算受限的线性表,它的限制是仅仅允许在表的一段进行插入,而在表的另一端进行删除。采用队列结构的集合,对元素的存取有以下特点。
(1) 先进先出:先存进去的元素,先取出来。
(2)队列的入口、出口各占一侧。
数组:
数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间中存放元素。就像一排房屋,有100个,每个房屋都有固定的编号,通过编号就可以找到具体的房屋。采用数组结构的集合,对元素的存取有以下特点。
(1)查找元素块:通过索引,可以快速的访问指定位置的元素。
(2)增删元素慢 :
指定索引位置增加元素:需要创建一个新数组,将指定新元素存储到指定索引位置,再把原数组元素根据索引,复制到新数组对应的索引位置。
指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应的索引位置,原数组中指定的索引位置不复制到新数组中。
链表:
链表:Linked List。是由一系列结点node(链表中每一个元素都成为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。链表结构有单向链表和双向链表。下面主要说单向链表结构。采用链表结构的集合,对于元素的存取有以下特点。
(1)多个结点之间,通过地址进行连接。
(2)查找元素慢:要查找元素,需要通过连接的节点,依次向后查找指定的元素。
(3)增删元素快:
增加元素:只需要修改连接下一个元素的地址即可。
删除元素:只需要修改连接下一个元素的地址即可
红黑树:
二叉数:binary tree 是每个结点不超过2的有序树(tree)。
二叉树就是每个节点最多有两个子树的树结构。顶上的叫根节点,两边的被叫做“左子树”和“右子树”。
红黑树:是二叉树的一种,红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树,这就意味着树的键值仍然是有序的。
红黑树的约束:
1.节点可以是红色的或者黑色的。
2.根节点是黑色的。
3.叶子节点(特指空节点)是黑色的
4.每个红色节点的子节点都是黑色的。
5.任何一个节点到其每一个叶子节点的所有路径上的黑节点数相同。
红黑树的特点:
速度快,趋近于平衡数,查找叶子元素最少和最多次数不多于二倍。
2.List集合
2.1概述:
list接口继承于collection接口,是单列集合的一个重要分支,习惯性的将实现了List接口的对象,称为List集合。List集合中允许出现重复的元素,所有的元素都是以线性方式进行存储的,在程序中可以通过索引去访问List集合中的元素。此外,List集合的另一个特点就是元素的存入顺序和取出顺序一致。
List接口的特点:
(1)它是一个元素存取有序的集合。
(2)它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素。
(3)集合中可以有重复元素,通过元素的equals方法,来比较是否为重复的元素。
2.2 List接口中常用的方法
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):用指定的元素替换集合中指定位置的元素,返回的是被替换的元素
public class Text {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("乔巴1");
list.add("乔巴2");
list.add("乔巴3");
list.add("乔巴4");
System.out.println(list);//[乔巴1, 乔巴2, 乔巴3, 乔巴4]
list.add(2,"乔巴");
System.out.println(list);//[乔巴1, 乔巴2, 乔巴, 乔巴3, 乔巴4]
String s = list.get(2);
System.out.println("List集合中索引2处的元素为"+s);//List集合中索引2处的元素为乔巴
String remove = list.remove(3);
System.out.println("List集合中索引3处的元素为"+remove+"被删除");//List集合中索引3处的元素为乔巴3被删除
System.out.println(list);//[乔巴1, 乔巴2, 乔巴, 乔巴4]
String s1 = list.set(2, "路飞");
System.out.println("List集合中索引2处的元素被换为"+s1);//List集合中索引2处的元素被换为乔巴
System.out.println(list);//[乔巴1, 乔巴2, 路飞, 乔巴4]
}
}
3.List 的子类
3.1 ArrayList集合
java.util.ArrayList 集合数据存储的结构是数组结构。元素增删慢,查找快。
3.2 LinkedList集合
Java.util.LinkedList 集合数据存储的结构是链表结构。方便元素的添加、删除。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。
LinkedList 是 list 的子类,List中的方法LinkedList都可以使用,,LinkedList集合也可以作为堆栈,队列的结构使用。
public class Text {
public static void main(String[] args) {
LinkedList<String> linkedList=new LinkedList<>();
linkedList.addFirst("路飞");
linkedList.addFirst("乔巴");
linkedList.addFirst("山治");
System.out.println(linkedList);//[山治, 乔巴, 路飞]
linkedList.addLast("索隆");
System.out.println(linkedList);//[山治, 乔巴, 路飞, 索隆]
System.out.println("返回集合的第一个元素"+linkedList.getFirst());//返回集合的第一个元素山治
System.out.println("返回集合的最后一个元素"+linkedList.getLast());//返回集合的最后一个元素索隆
linkedList.removeFirst();//移除集合的第一个元素
System.out.println(linkedList);//[乔巴, 路飞, 索隆]
linkedList.removeLast();//移除元素的最后一个元素
System.out.println(linkedList);//[乔巴, 路飞]
while(!linkedList.isEmpty()){
System.out.println("弹出栈顶的元素"+linkedList.pop());
//弹出栈顶的元素乔巴 弹出栈顶的元素路飞
}
linkedList.push("娜美");
System.out.println(linkedList);//[娜美]
}
}
4 set接口
java.util.set接口是和java.util.list接口一样,是继承Collection接口,Set接口和Collection接口的方法基本一致,没有对Collection的方法就行扩充,但是它比Collection 接口更加严格了。与List集合不同的是,Set集合中的元素无序,并且保证集合中的元素不重复。
Set集合有多个子类:java.util.HashSet 、java.util.LinkedHashSet这两个集合。
4.1 HashSet集合
java.util.HashSet是Set接口的一个实现类,它存储的元素是不可重复的,并且元素都是无序的(存和取的顺序不一样)。
HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。它能保证元素的唯一性,依赖于:HashCode与equals方法。
public class Text {
public static void main(String[] args) {
HashSet<String> hs=new HashSet<>();
hs.add("路飞");
hs.add("索隆");
hs.add("乔巴");
hs.add("娜美");
hs.add("路飞");
System.out.println(hs);//[乔巴, 娜美, 索隆, 路飞]
}
}
从上述代码的输出可以看出,元素的输出和存入的顺序不同,并且不能存储重复元素。
4.2 HashSet集合存储数据的结构(哈希表)
哈希表:在JDK1.8之前,哈希表的底层是素组+链表实现的,使用了链表处理冲突,同一个哈希值的链表存储在一个链表里。但是当位于一个桶的元素较多时,即哈希值的相同的元素较多时,通过Key值一次查找的效率就比较低。在JDK1.8之后,哈希表存储采用数组+链表+红黑树来实现,当链表的长度超过阈值8时,将链表转换成红黑树,这样就增加了查找的效率。
哈希表是有数组+链表+红黑树实现的。
HashSet具体是怎么存储元素的,如下图:
JDK 1.8 之后,大大优化了HashSet的性能,要保证HashSet集合中元素的唯一,就必须利用对象的HashCode和equals方法来确定。如果我们想要在HashSet集合中存储自定义的对象,就必须覆盖重写HashCode和equals方法。
4.3 HashSet存储自定义类型元素
当HashSet中存放自定义对象时,一样要重写对象中的HashCode和equals方法,用来确保集合中元素的唯一性。
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return getAge() == student.getAge() &&
Objects.equals(getName(), student.getName());
}
@Override
public int hashCode() {
return Objects.hash(getName(), getAge());
}
}
public class Text {
public static void main(String[] args) {
HashSet<Student> hs=new HashSet<>();
hs.add(new Student("路飞",18));
hs.add(new Student("索隆",19));
hs.add(new Student("山治",10));
hs.add(new Student("路飞",18));
System.out.println(hs);
//[Student{name='路飞', age=18}, Student{name='索隆', age=19}, Student{name='山治', age=10}]
}
}
4.3 LinkedHashSet
目前我们知道HashSet集合可以保证元素的唯一性,但是元素的无序的,如果我们想要元素唯一,并且存取顺序一致,应该怎么做。在HashSet集合下,有一个java.util.LinkedHashSet,它是有链表和哈希表组合成的一个数组存储节结构。
public class Text {
public static void main(String[] args) {
LinkedHashSet<String> lhs=new LinkedHashSet<>();
lhs.add("路飞");
lhs.add("索隆");
lhs.add("山治");
lhs.add("乔巴");
lhs.add("路飞");
System.out.println(lhs);//[路飞, 索隆, 山治, 乔巴]
}
}
上述代码所示:LinkedHashSet集合,保证了元素的唯一性,并且确保了元素的存取顺序。
5.可变参数
在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且参数的类型一致,我们可以对其简化成一下格式:
修饰符 返回值类型 方法名 ( 参数类型... 形参名 ) { }
上述格式等价于:修饰符 返回值类型 方法名( 参数类型[ ] 形参名 ) { }
第二种格式在调用时必须先创建数组,第一种格式就可以直接传递数据,就称为可变参数。
public class Text {
public int sum1(int[] arr){
int sum=0;
for (int i : arr) {
sum+=i;
}
return sum;
}
public int sum2(int... arr){
int sum=0;
for (int i : arr) {
sum+=i;
}
return sum;
}
public static void main(String[] args) {
Text t=new Text();
int[] arr1={1,2,3,4,5,6,7,8,9,10};
int i = t.sum1(arr1);
System.out.println(i);
int i1 = t.sum2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(i1);
}
}
6.Colltions
6.1 Collections的常用功能
java.utils.Collections是集合工具类,用来对集合进行操作,常用的方法如下:
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> ) :将集合中元素按照指定规则排序。
public class Text {
public static void main(String[] args) {
ArrayList<Integer> list=new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list);//[1, 2, 3, 4]
//往集合中添加元素
Collections.addAll(list,5,6,7,8);
System.out.println(list);//[1, 2, 3, 4, 5, 6, 7, 8]
//打乱集合的顺序
Collections.shuffle(list);
System.out.println(list);//[7, 2, 3, 8, 6, 4, 5, 1]
Collections.sort(list);//将集合按默认的顺序排序
System.out.println(list);//[1, 2, 3, 4, 5, 6, 7, 8]
}
}
6.2 Comparator 比较器
在Collections工具类中有默认排序方法:public static <T> void sort(List<T> list) :将集合中元素按照默认规则排序。
但是如果集合中存储的事String类型的字符串呢?
public class Text {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
//往集合中添加元素
Collections.addAll(list,"cbc","ads","ddf","bfd");
System.out.println(list);//[cbc, ads, ddf, bfd]
Collections.sort(list);//将集合按默认的顺序排序
System.out.println(list);//[ads, bfd, cbc, ddf]
}
}
我们使用的是默认的规则完成字符串的排序,那么默认规则是怎么定义出来的呢?
说到排序了,简单的说就是两个对象之间比较大小,那么在JAVA中提供了两种比较实现的方式,一种是比较死板的
采用java.lang.Comparable 接口去实现,一种是灵活的当我需要做排序的时候在去选择的java.util.Comparator 接口完成。
Collections工具类中的public static <T> void sort(List<T> list) 这个方法完成的排序,实际上要求了被排序的类型
需要实现Comparable接口完成比较的功能,在String类型上如下:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { }
String类实现了这个接口,并完成了比较规则的定义,但是这样就把这种规则写死了,那比如我想要字符串按照第
一个字符降序排列,那么这样就要修改String的源代码,这是不可能的了,那么这个时候我们可以使用:
public static <T> void sort(List<T> list,Comparator<? super T> ) 方法灵活的完成,这个里面就涉及到了
Comparator这个接口,位于位于java.util包下,排序是comparator能实现的功能之一,该接口代表一个比较器,比
较器具有可比性!顾名思义就是做排序的,通俗地讲需要比较两个对象谁排在前谁排在后,那么比较的方法就是:
public int compare(String o1, String o2) :比较其两个参数的顺序。
两个对象比较的结果有三种:大于,等于,小于。
如果要按照升序排序, 则o1 小于o2,返回(负数),相等返回0,01大于02返回(正数) 如果要按照
降序排序 则o1 小于o2,返回(正数),相等返回0,01大于02返回(负数)
public class Text {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
//往集合中添加元素
Collections.addAll(list,"cbc","ads","ddf","bfd");
System.out.println(list);//[cbc, ads, ddf, bfd]
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);//[ddf, cbc, bfd, ads] 降序
}
}
6.3 Comparable 和Comparator 两个接口的区别
Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法
被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排 序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以 用作有序映射中的键或有序集合中的元素,无需指定比较器。
Comparator: 强行对某个对象进行整体排序。可以将Comparator 传递给sort方法(如Collections.sort或
Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有 序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。
public class Demo {
// 创建四个学生对象 存储到集合中
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("rose",18));
list.add(new Student("jack",16));
list.add(new Student("abc",16));
list.add(new Student("ace",17));
list.add(new Student("mark",16));
/*
让学生 按照年龄排序 升序
*/
// Collections.sort(list);//要求 该list中元素类型 必须实现比较器Comparable接口
for (Student student : list) {
System.out.println(student);
}
}
}
发现,当我们调用Collections.sort()方法的时候 程序报错了。 原因:如果想要集合中的元素完成排序,那么必须要实现比较器Comparable接口。于是我们就完成了Student类的一个实现,如下:
public class Student implements Comparable<Student>{
....
@Override
public int compareTo(Student o) {
return this.age‐o.age;//升序
}
}
再次测试,代码就OK 了效果如下:
Student{name='jack', age=16}
Student{name='abc', age=16}
Student{name='mark', age=16}
Student{name='ace', age=17}
Student{name='rose', age=18}