Day03
第一章 数据结构
1.1 了解数据结构的作用
了解数据结构有利于理解我们所使用工具的特点,明白它的底层原理,面对问题的时候选择一个合适的工具。
1.2 常见数据结构举例
栈:stack
栈是一种运算受限的线性表,限制在于:仅允许在标的一端进行插入和删除操作。
特点:
- 进栈与出栈都在栈的顶端
- 先进先出,后进后出,类似于一个放饼干的罐子,先放进去的饼干在最后才能拿出来。
两个名词
- 压栈:元素存入栈中
- 弹栈:从栈顶取出一个元素
队列:queue
同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。
采用该结构的集合有以下特点:
- 先进先出
- 出口入口各占一侧
数组:Array
是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。每个元素有固定的编号。
特点:
- 查找元素快 因为开辟的空间是连续的有固定的编号,根据编号索引
- 增删元素慢
指定索引位置增加元素:
新建一个新数组,先将新元素添加在数组的末尾,再建一个新数组,把新元素填在指定位置,再复制原数组的元素。
指定索引位置删除:
所以增删不方便的原因是,要创建新数组,不能直接增减。
链表:Linked list
由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时i动态生成,每个节点由数据域和指针域,指针域指向下一个数据域的地址。
采用该结构的集合,对元素的存取有如下的特点:
- 查找元素慢,需要通过节点连接依次查找
- 增删元素快,增时不需要创建新表,直接修改对应的节点和前一个指针的指向,并使新节点的指针域指向原来下一个节点。删除则移除那个节点,使节点前一个指针域直接指向后一个数据域。
- 增:
- 减:
二叉树:binary tree
是每个结点不超过2的有序树(tree),二叉树是每个节点最多有两个子树的树结构。顶上的叫根结点,两边被称作“左子树”和“右子树”。
红黑树
- 节点可以是红色的或者黑色的
- 根节点是黑色的
- 叶子节点(特指空节点)是黑色的
- 每个红色节点的子节点都是黑色的
- 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同
红黑树的特点:
速度特别快,趋近平衡树,查找叶子元素最少和最多次数不多于二倍
第二章 List集合
1.1LIst接口的特点
- 是一个元素存取有序的集合,元素在集合中的存储顺序与存入顺序一致
- 可以通过索引获得指定元素
- 集合中可以有重复的元素,是否重复采用元素的equals方法判断,比如说自定义一个类,默认是比较地址,可以根据自己需求改写equals方法。
常用方法
- 添加元素
public void add(int index,E element)
- 删除元素
public E remove(int index)
返回被删除的元素 - 查询元素
public E get(int index)
返回被索引的元素 - 替换(设定值)
public E set(int index,E element)
返回被替换前的index处的元素。
需要注意的是,对于添加元素,还存在默认方法,加在集合的尾部。而使用指定位置添加元素时,要保证在这个位置上已经有元素存在,否则会报错。
第三章 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 getFrist()
得到表中第一个元素(最后一个同理) -
public E removeFirst()
移除第一个元素,并返回被移除的元素 -
public E pop()
弹出此表表示的栈中栈顶元素,之后栈中不存在钙元素 -
public void push(E e)
将一个元素压入该表表示的堆栈、 -
public boolean isEmpty()
判断是否包含元素public static void main(String[] args) { LinkedList linklist = new LinkedList(); linklist.add(55); linklist.add(66); linklist.add(77); System.out.println(linklist);// [55,66,77] System.out.println(linklist.pop());//弹出栈顶元素55 System.out.println(linklist);//[66,77] linklist.push(456789);//压入栈顶 System.out.println(linklist);[456789,66,77] }
链表表示为堆栈的示意图:类似于一个“转置”的过程
第四章 Set接口
java.util.Set
接口和java.util.List
接口一样,同样继承自Collection
接口。Set接口相比于Collection接口没有功能扩充,但是要求更加严格,不能有重复的元素存在,是否重复根据hashCode和equals来判断。与List接口相比,Set接口中的元素时无序的。
4.1 HashSet集合
HashSet
是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。
public static void main(String[] args) {
HashSet set = new HashSet();
set.add(123);
set.add(9);
set.add(123);
set.add("sdasdsa");
set.add(6);
System.out.println(set);//[sdasdsa, 6, 9, 123],重复的元素无法再次存入
}
4.2 HashSet中的数据结构
在JDK1.8之前,哈希表底层采用数组+链表实现,链表长度过长时即hash值相等的元素较多时,通过key值依次查找的效率较低,会影响查询效率。
JDK1.8中,哈希表存储采用数组+链表+红黑树实现。链表长度>阈值8时,会自动转换成红黑树,减少查询时间。
4.3 HashSet存储自定义类型元素
“如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。”
import java.util.HashSet;
import java.util.Objects;
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSet set= new HashSet();
set.add(new Student("first",18));
set.add(new Student("second",19));
set.add(new Student("third",20));
set.add(new Student("fourth",21));
set.add(new Student("fourth",25));
set.add(new Student("first",25));
set.add(new Student("first",18));
set.add(new Student("Last",18));
System.out.println(set);
//[first 25, Last 18, third 20, fourth 21, first 18, fourth 25, second 19]
}
}
class Student{
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if(this == o) {
return false;
}
if(o == null || o.getClass() != this.getClass()) {
return false;
}
Student studenttemp = (Student)o;
return studenttemp.age == this.age && studenttemp.name==this.name;
}
@Override
public int hashCode() {
return Objects.hash(name,age);
}
@Override
public String toString() {
return name+" "+age;
}
}
4.4 LinkedHashSet
java.util.LinkedHashSet
是HashSet下面有一个子类,它是链表和哈希表组合的一个数据存储结构。它可以保证集合的有序。
public static void main(String[] args) {
// TODO Auto-generated method stub
LinkedHashSet<String> set = new LinkedHashSet<String>();
set.add("aaa");
set.add("bbb");
set.add("ccc");
set.add("aaa");
set.add("ddd");
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.print(it.next()+" ");//aaa bbb ccc ddd
}
}
第五章 可变参数
JDK1.5之后,定义一个方法时,需要多个的同一类参数,可以简化成
修饰符 返回值类型 方法名(参数类型... 参数名){方法体}
它等价于修饰符 返回值类型 方法名(参数类型[] 参数名){方法体}
,...
称为可变参数
区别第二种定义时,只能传递数组,而前者可以直接传递数据,在编译时会自动封装成数组,在调用时就不用手动创建数组(更加方便)。
其实编译成的class文件,将这些元素先封装到一个数组中,在进行传递。这些动作都在编译.class文件时,自动完成了。
public static void main(String[] args) {
int [] arr = {1,2,3,4,5,6,7,8,9};
System.out.println(getSum(arr));//45
System.out.println(getSum(1,2,3,4,5,6,7,8,9));//45
}
public static Integer getSum(int... a) {
int returnsum=0;
for(int b: a) {
returnsum+=b;
}
return returnsum;
}
上述add方法在同一个类中,只能存在一个。因为会发生调用的不确定性。
方法的参数中含有可变参数时,可变参数必须要放在参数列表的最后一个。
第六章 Collections
6.1 常用功能
-
增加一系列元素
public boolean addAll(Collection<T>,T... Elements)
-
打乱顺序
public static void shuffle(List<?> list)
-
public static <T> sort(List<?> list)
将集合中元素按照默认规则排序,如数字默认为按大小升序排列 -
public static <T> sort(List<?> list,Comparator<? super T>)
将集合中的元素按照用户自定义的规则排序,如将数字进行降序排列public class test { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); Collections.addAll(list, 5,6,7,8,9,10); Collections.shuffle(list); System.out.println(list); Collections.sort(list); System.out.println(list); Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2-o1;//降序排列 } }); System.out.println(list); } }
6.2 Comparator比较器
那么在JAVA中提供了两种比较实现的方式,一种是比较死板的采用
java.lang.Comparable
接口去实现,一种是灵活的当我需要做排序的时候在去选择的java.util.Comparator
接口完成。
如果使用public <T> void sort(List<?> list)
进行比较时,含义是已经实现了Compareble接口。例如String类中就已经实现了这一接口,所以可以直接调用此方法进行比较,但是它的缺点是,规则已经定死,如果想进行逆序排列或者其他规则,则要重写此方法。
这种情况下可以采用public <T> void sort(List<T> list,Comparator<? extends T>)
的方式,通过实现public int compare(E e1,E e2)
来灵活确定规则。
两个对象比较的结果有三种:大于,等于,小于。
如果要按照升序排序,
则o1 小于o2,返回(负数),相等返回0,01大于02返回(正数)
如果要按照降序排序
则o1 小于o2,返回(正数),相等返回0,01大于02返回(负数)
简单来说就是,若想升序则return o1-o2;反之是o2-o1;
6.3 Comparator接口与Comparable接口的区别
Comparable接口在使用前需要在类中重写方法,对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,重写后可以直接调用public <T> void sort(List<?> list)
进行比较,无需指定比较器,缺点是规则一旦确定修改比较麻烦。
Comparator 接口在使用定义Compare比较器,根据自身需求以及没有自然顺序的对象Collection设定规则,使用灵活,但是每次都要定义,如果需要多次比较且规则单一,还是Comparable比较方便。
6.4 练习
创建一个学生类,存储到ArrayList集合中完成指定排序操作。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class test {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("cbx",20));
list.add(new Student("aby",19));
list.add(new Student("bbz",18));
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge()-o2.getAge();
}
});//按照年龄升序排列
System.out.println(list);
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// TODO Auto-generated method stub
return o2.getName().charAt(2)-o2.getName().charAt(2);
}
});//按照姓名最后一个字母降序排列
System.out.println(list);
}
}
class Student{
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "姓名:"+name+" "+"年龄:"+age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
}
输出结果:
[姓名:bbz 年龄:18, 姓名:aby 年龄:19, 姓名:cbx 年龄:20]
[姓名:bbz 年龄:18, 姓名:aby 年龄:19, 姓名:cbx 年龄:20]
上述方式是采用Comparator接口的方式。
Comparable接口方式:
需要修改Student类中的内容
public class test {
public static void main(String[] args) {
//学生类创建内容与先前保持一致
Collections.sort(list);
System.out.println(list);//[姓名:cbx 年龄:20, 姓名:aby 年龄:19, 姓名:bbz 年龄:18]
}
}
class Student implements Comparable<Student>{
...
@Override
public int compareTo(Student o) {
return o.age-this.age;
}//以年龄为比较降序排列
}
拓展:
//还可以进一步定义规则
...
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
if(o1.getAge() != o2.getAge()){
return o1.getAge()-o2.getAge();}
else{
return o1.getName().cahrAt(0)-o2.getName().cahrAt(0);
}
}
});
...