常见的几种数据结构:
* 1.堆栈:先进后出
* 2.队列:先进先出
* 3.数组:查找快,增删慢
* 4.链表:查找慢,增删快
import java.util.LinkedList;
/*
* java集合的根接口 Collection
* 共性的方法:增删改查
* 增:add(E e);//addAll(Collection c)
* 删:remove(Object obj);
* 改:无
* 查:size()
* 其他:toArray();clear();isEmpty();
*
* 分为两个子接口:List,Set
* 1.List:接口,有实现类:ArrayList,LinkedList,Vector
* 特点:a.有序的,(这里的有序 不是指123,或者abc这种自然顺序,指的存和取的顺序一致)
* b.有索引(有下标)
* c.元素可重复
* List接口中定义方法:增删改查
* 增加: add(E e);add(int index,E e);//在指定下标处增加一个元素
* 删除: remove(Object obj); remove(int index);//删除指定下标元素
* 修改: set(int index,E e);//把指定下标元素改为新的元素
* 查询: get(int index);//获取指定下标的元素
* List接口下各种实现类的数据结构特点:
* ArrayList: 内部采用动态数组结构:查找快,增删慢
* LinkedList: 内部采用双向链表结构:查找慢,增删快
* Vector: 内部采用数组结构:查找快,增删慢
* 说明:Vector是从JDK1.0 开始有的
* Collection集合根接口从JDK1.2开始的
* ===============================================
* 实际上ArrayList和Vector中基本没有特有的方法,这些实现类的方法基本上都是List中的方法
* LinkedList特有的方法:
* 第一组:
* addFirst(E e),addLast(E e);
* 第二组:
* public E removeFirst();//返回并删除首元素
* public E removeLast();
* 第三组:
* public E getFirst();//返回首元素
* public E getLast();
* 第四组:
* public E pop();//出栈,弹栈,就是从集合中删除元素(哪一个:集合首个元素,类似于 removeFirst)
* public void push(E e);//压栈,就是向集合添加一个元素(添加到集合的第一个,类似于add,addFirst)
*
*
* 2.Set:接口,有实现类,HashSet,LinkedHashSet,TreeSet
* 特点:a.元素不重复
* b.无索引(无下标)
* c.无序(有例外,LinkedHashSet,TreeSet)
* Set接口中定义方法:增删改查(特别好记,Set接口中没有特有方法完全和Collection接口一模一样)
*
* Set接口下各种实现类的数据结构特点:
* HashSet: 内部采用哈希表结构
* 特点:查找较快,增删也较快
* LinkedHashSet: 内部采用 链表结构+哈希表结构 组合结构
* 特点:查找较快,增删也较快
* HashSet和LinkedHashSet特有的方法:无
* 全部都是和Set接口中一样,和Collection接口中一样
*/
public class LinkedListDemo01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
demo01();
}
/*
* LinkedList特有的方法
*/
public static void demo01(){
//创建一个对象
LinkedList names = new LinkedList();
names.add("jack");
names.add("rose");
names.add("lilei");
names.add("rose");
//添加
// names.addFirst("hanmeimei");
// names.addLast("Tom");
//获取(仅仅取出元素,但是不删除)
// String s1 = names.getFirst();
// System.out.println(s1);
// String s2 = names.getLast();
// System.out.println(s2);
//删除元素(不仅取出元素,而且会在集合中删除)
// String s1 = names.removeFirst();
// System.out.println(s1);
// String s2 = names.removeLast();
// System.out.println(s2);
//pop 出栈方法
// String s1 = names.pop();
// System.out.println(s1);
// String s2 = names.pop();
// System.out.println(s2);
//push 入栈方法
names.push("Tom");
names.push("Jirui");
System.out.println(names);
}
}
*
* HashSet 判断元素是否重复是根据两个条件
*
* 1.看新元素的哈希值 和 所有旧元素是否相同,如果不相同那么不重复,存储
*
* 2.如果哈希值相同了,调用equals方法,拿新元素和哈希值相同的那个旧元素,返回值true,那么判断重复元素,不存储
* 返回值false 判断不重复 可以存储
*
*
* 哈希表:底层是 数组结构+链表结构 特点:查找较快,增删较快 哈希表又称散列表,是一种能将关键字映射成存储地址的记录存储技术。
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,
以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。Hash就是找到一种数据内容和数据存放地址之间的映射关系。
* 对象的哈希值:
* 实际上java中所有的对象 都有一个字符串表示:toString(),默认:包名.类名@哈希值
* 实际上java中所有的对象 都有一个数字的表示:hashCode();//返回的就是一个数字,就是我们说对象的哈希值
*
*
* 字符串的哈希值:
* 字符串类的哈希值 自己重写了hashCode,计算哈希值只跟内容有关系,所有内容相同的字符串,哈希值必定相同
* 内容不同的字符串,哈希值还有可能相同
*
*
*
*/
public class StringHashDemo01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
String s1 = new String("重地");
String s2 = new String("通话");
int h1 = s1.hashCode();
int h2 = s2.hashCode();
//System.out.println(h1==h2);
//哈希值是根据地址值计算的,如果String类没有重写hashCode那么算出来的哈希值应该是不一样
//因为String类重写了 hashCode,所以不同的地址和hash值的计算已经没有关系
//System.out.println(s1==s2);//false
System.out.println(h1);//96354
System.out.println(h2);
// System.out.println(s1);
// System.out.println(s2);
//我们看了String类的hashCode方法,字符串的哈希值,只跟内容有关系
//可能不可能出现 字符串的内容不同 但是哈希值相同
//比如:"abc"和"acD"
//比如: "重地"和 "通话"
}
/*
* 对象的哈希值
*/
public static void demo01(){
//创建Person对象
Person p1 = new Person();
int hash = p1.hashCode();
//hash就是p1的哈希值
System.out.println(hash);
System.out.println(p1.toString());
//我们骗了19天,打印对象默认打印出来的不是地址值 是hash值
//哈希值是根据地址值算出来的,怎么算的我们不知道
//实际上 由地址值 计算哈希值 由一个算法计算 散列算法---哈希算法
//在我们java程序中能不能打印出对象 真正的地址值(不能)
//但是地址值 是真正存在的 对象中存储的就是真正的地址值 但是打印的时候打印出来是哈希值
Person p2 = new Person();
int hash2 = p2.hashCode();
System.out.println(hash2);
//不同的对象 地址值肯定不一样,但是哈希值有可能一样吗?
}
}
/*
* 使用HashSet存储自定义的元素:存储Student对象
*
* 我们要求:如果一个学生对象的年龄和姓名都相同,我们不让HashSet存储它
*
* 以后写一个类:
* 1.封装:
* 2.构造(全参和无参)
* 3.toString(方便输出对象)
* 如果这个类还要存储到集合中最好
* 4.hashCode
* 5.euqals
*
*/
public class Student {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(int age, String name) {
super();
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
//自己重写hachCode
// 20 abc
// 20 acD
// 19 acE
// @Override
// public int hashCode() {
// //我们目的,让年龄相同并且名字相同的对象返回的哈希值也相同
// return this.age * 31 + this.name.hashCode()*49;
// }
//Eclipse自动生成的hashCode
//自己重写equals方法,让年龄相同,名字相同两个对象调用equals返回值是true
// @Override
// public boolean equals(Object obj) {
// // TODO Auto-generated method stub
// Student s = (Student)obj;
// if(this.age != s.getAge()){
// return false;
// }
// if(!this.name.equals(s.getName())){
// return false;
// }
// return true;
// }
//Eclipse自动生成的equals方法
}
/*
* LinkedHashSet和HashSet的不同点:
* HashSet是无序的
* LinkedHashSet是有序的
*
* contains方法和add方法的原理
*
* 1.如果是ArrayList存储自定义元素 ,那么这个自定义类型 只要重写 equals
*
* 2.如果是HashSet存储自定义元素,那么这个自定义类型 必须重写两个 hashCode,equals
*
*/
/*
* Collection接口:
* add(E e) remove(Object o),size(),toArray();isEmpty();contains()
* 迭代器
* 获取迭代器: Iterator it = 集合对象.iterator();
* 使用迭代器:
* it.hasNext(); it.next();
*
* List和Set
* 能够说出List集合特点:有序,索引和重复
* 能够说出Set集合的特点:无索引,不重复,无序的(但是LinkedHashSet,TreeSet是有序的)
*
* 使用List存储的数据结构:ArrayList是数组结构 LinkedList是链表结构
*
* 说出哈希表的特点:数组结构+链表结构: 查找较快,增删较快
*
* 使用HashSet集合存储自定义元素(保证元素的内容相同不能存储)
* 快捷键:alt+shift+s,h
*
* 说出判断集合元素唯一的原理
* Collection中有contains(Object o);
* ArrayList中 只调用equals比较
* HashSet中 调用hashCode 和 equals
*
* 冒泡排序:把一个数组里面的内容 按照从小到大 或者从大到小
*/
public class BubbleDemo {
public static void main(String[] args) {
int[] nums = {7,65,10,532,47,45,234,84,3};
int len = nums.length;
//外层控制趟数
for (int i = 0; i < len-1; i++) {
//里控制次数
for(int j = 0;j < len-1-i;j++){
//nums[j] 和 nums[j+1]
if(nums[j] > nums[j+1]){
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
}
}
冒泡排序优化版:
package com.baidu_01;
import java.util.Arrays;
public class Test3 {
public static void main(String[] args) {
//int[] arr = {1,8,5,3,7,6};
int[] arr = {1,2,3,4,5,6};
boolean b = true ;
for(int i = 0; i < arr.length-1 ; i++) {
//b = true;
for(int j = 0; j < arr.length - 1 -i;j++) {
if(arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
b = false;
}
}
if(b) {
System.out.println("遍历了一次");
break;
}
}
System.out.println(Arrays.toString(arr));
}
}
3.TreeSet类
TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0。向TreeSet中添加的应该是同一个类的对象,且最好是不可变对象。
1.自然排序
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。
如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0
2.定制排序
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法,该方法用于比较o1和o2的大小:如果该方法返回正整数,则表示o1大于o2
;如果方法返回0,则表示o1等于o2,如果该方法返回负整数,则表示o1小于o2。
Java静态代码块、构造代码块、构造方法的执行顺序 的执行顺序
静态代码优先于非静态的代码,是因为被static修饰的成员都是类成员,会随着JVM加载类的时候加载而执行,而没有被static修饰的成员也被称为实例成员,需要创建对象才会随之加载到堆内存。所以静态的会优先非静态的。
执行构造器(构造方法)的时候,在执行方法体之前存在隐式三步:
1,super语句,可能出现以下三种情况:
1)构造方法体的第一行是this语句,则不会执行隐式三步,
2)构造方法体的第一行是super语句,则调用相应的父类的构造方法,
3)构造方法体的第一行既不是this语句也不是super语句,则隐式调用super(),即其父类的默认构造方法,这也是为什么一个父类通常要提供默认构造方法的原因;
2,初始化非静态变量;
3,构造代码块。
由此可知,构造代码块优先于构造方法的方法体,但是this关键字跟super关键字不能同时出现,而且只能在代码的第一行。如果出现了this关键字,隐式三步就不会执行。
例如,分析下面的代码及执行结果,已经用注释标出了执行步骤Step 1–Step 7。
也就是说,当递归调用多个构造方法的时候,构造代码块只会在最后的(也即方法体第一行不是this语句的)那个构造方法执行之前执行!
public class Test {
public static int a = 0;
static {// Step 1
a = 10;
System.out.println("静态代码块在执行a=" + a);
}
{// Step 4
a = 8;
System.out.println("非静态代码块(构造代码块)在执行a=" + a);
}
public Test() {
this("调用带参构造方法1,a=" + a); // Step 2
System.out.println("无参构造方法在执行a=" + a);// Step 7
}
public Test(String n) {
this(n, "调用带参构造方法2,a=" + a); // Step 3
System.out.println("带参构造方法1在执行a=" + a); // Step 6
}
public Test(String s1, String s2) {
System.out.println(s1 + ";" + s2);// Step 5
}
public static void main(String[] args) {
Test t = null;// JVM加载Test类,静态代码块执行
System.out.println("下面new一个Test实例:");
t = new Test();
}
public boolean contains(Object c) : 判断是否包含元素
public boolean isEmpty() : 判断是否为空
public int size() : 获取集合长度
Collection中主要方法:
boolean add(E e) : 添加元素
boolean remove(Object o) : 删除元素
void clear() : 清空集合
boolean contains(Object o1) : 判断是否包含某元素
boolean isEmpty () : 判断是否为空
int size() : 获取集合长度
}
1
33
执行结果:
静态代码块在执行a=10
下面new一个Test实例:
非静态代码块(构造代码块)在执行a=8
调用带参构造方法1,a=10;调用带参构造方法2,a=10
带参构造方法1在执行a=8
无参构造方法在执行a=8
*/
/*
Arrays : 查API
static void sort(Object[] obj) : 对传进来的基本类型数组进行排序
static void toString(Objec[] a) : 对传入的数组内容以字符串的形式表现出来.
方法重写:字符类出现了一摸一样的方法(注意:返回值类型可以是子父类)
Override和Overload的区别?Overload能改变返回值类型吗?
overload可以改变返回值类型,只看参数列表.
方法重载:本类中出现的方法名一样,参数列表不同的方法,与返回值类型无关.
子类对象调用方法的时候:
先找子类本身,再找父类.
final关键字修饰局部变量:
1.基本类型,是指不能被改变.
2.引用类型,是地址值不能被改变,对象中的属性可以改变
final修饰变量的初始化时:
1.显示初始化;
2.在对象构造完毕前即可
面试:
要求使用已知的变量,在控制台输出30,20,10
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show () {
int num = 30;
syso();
syso();
syso();
}
}
}
class InnerClassTest {
public static void main(String[] args ) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
package , import , class 顺序为:package - import - class 按照这个顺序执行
代码块的概述:在java中,使用{}括起来的代码被称为代码块.
A.代码块分为:局部代码块,构造代码块,静态代码块,同步代码块
代码块应用:
a.局部代码块:在方法中出现;限定变量声明周期,及早释放,提高内存利用率.
b.构造代码块(初始化块) : 在类中方法外出现;多个构造方法中相同的代码块放到一起,每次调用构造都指向,并且在构造方法前指向
c.静态代码块:在类中方法外出现,并加上static 修饰;用于给类进行初始化,在加载的时候就指向,并且只执行一次.
一般用于加载驱动.
多态调用方法这么执行:
成员变量:编译看左边(父类),运行看左边(父类);
成员方法:编译看左边(父类),运行看右边(子类).动态绑定
静态方法:编译看左边(父类),运行看左边(父类);
(静态和类相关,算不上重写,所以,访问换是左边的)
只有非静态方法的成员方法,编译看左边,运行看右边.
内部类访问特点:
外部类访问内部类必须创建对象.
外部类名.内部类名 对象名 = 外部类对象.内部类对象
静态成员内部类:
static :成员内部类被静态修饰后的访问方式是:
外部类名.内部类名 对象名 = 外部类.内部类对象;
面试:
class A {
public void show () {
show2();
}
public void show2() {
syso("我");
}
}
class B extends A{
show(){
show2();
}
show 2 (){
syso("爱")
}
}
class c extends B{
show(){
show2();
}
show 2 (){
syso("你")
}
}
public class Test {
main {
A a = new B();
a.show();
B b = new C();
b.show();
}
}
面试题:
一个抽象类如果没有抽象方法,可不可以订阅为抽象类,?有什么意思?
可以,这么做只有一个目的,就是不让其他类创建本类对象,交给子类完成.
class Test {
public static void main(String[] stgs ) {
//Outer.method().show();
Inter i = Outer.method();
i.show();
}
}
//按照要求补齐代码
interface Inter {
void show();
}
class Outer {
public static Inter method() {
return new Inter() {
public void show() {
syso("show");
}
}
}
}
方法重写注意事项:
1.父类中私有方法不能被重写.(父类私有方法子类根本就无法继承)
2.子类重写父类方法时,访问权限不能更低.
最后就一致
3.父类静态方法,子类也必须通过静态方法进行重写.
其实这个算不算方法重写,但是现象确实如此,至于为什么算不上方法重写
子类重写父类方法,最好声明一摸一样.
例题:
class Fu {
public Person show() {
}
}
class Zi extends Fu {
public Student show() {
}
}
class Person {
}
class Student extends Person {
}
运算符:instanceof : 用法:
是双目运算符,左面的操作是一个对象实例,右面是一个类.当左面的对象是右面的类创建的对象时,该运算符
运算的结果是true,否则是false;
说明:
1.一个类的实例包括本身的实例,以及所有直接或间接子类的实例
2.instanceof左边操作元显示的类型与右边操作元必须是同种类或有继承关系
即位于继承树的同一个分支上,否则会编译出错.
冒泡排序:让数组中的元素,从小到大存储.
相邻比较思想. 我们需要比较多少趟
第一趟: 我们要比较几次 len-1; 冒泡排序 : 如果一个数组的长度还是len
那么要比较 len-1趟
下标 0-1
//控制躺数
下标 1-2 for(int i = 0 ; i < len-1; i ++) {
下标 2-3 for(int j = 0 ; j < len-1; j++) {
下标 3-4 比较num3[j]和num3[j+1]
下标 4-5
下标 5-6
}
}
第二趟:
len-1-1;
0-1
1-2
2-3
3-4
4-5