初步算法:
冒泡排序:
//每次从数组中找出最大值放到数组后面去。
int[] arr={5,8,4,7,6};
int m;
for (int i = 0; i <2; i++) {
for (int j = 0; j < arr.length-1-i; j++) {
if (arr[j]>arr[j+1]){
m=arr[j+1];
arr[j+1]=arr[j];
arr[j]=m;
}
}
}
String s1=Arrays.toString(arr);
System.out.println(s1);
总体上来说这种排序是让第一项与第二项比较然后排序,通过这种方法可以确保每次筛选出一个最大值或一个最小值。
也可以根据实际需要排序的数组调整循环,来简短时间消耗;
选择排序:
//每次将最小值放到最前面
int[] arr1={5,8,4,7,6,2,3,9,5};
for (int i = 0; i < arr1.length-1; i++) {
int minindex=i;
for (int j = i+1; j < arr1.length; j++) {
if (arr1[minindex]>arr1[j]){
minindex=j;
// m=arr1[j];
// arr1[j]=arr1[i];
// arr1[i]=m;//省略了每次找到较小值都需要交换的步骤,缩减了程序
}
}
if (i!=minindex){
m=arr1[minindex];
arr1[minindex]=arr1[i];
arr1[i]=m;
}
}
String s2=Arrays.toString(arr1);
System.out.println(s2);
每次从数组中找出一个最值放到前面(即两个位置的数组值交换)
二分查找:
需要目标数组是有序的(从大到小或从小到大)
int[] arr={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50};
//寻找x
Random rd=new Random();
int x=rd.nextInt(50)+1;
System.out.println(x);
int n=arr.length-1,m=0,i=0;
while(m<=n){
if (arr[(n+m)/2]>x){
n=(n+m)/2-1;
}else if (arr[(n+m)/2]<x){
m=(n+m)/2+1;
}else {
System.out.println(x+"的位置为:"+(n+m)/2);
break;
}
/*if (x==arr[arr.length-1]){
System.out.println(x+"的位置是:"+(arr.length-1));
break;
}*/
System.out.println(++i);
}
System.out.println(Arrays.binarySearch(arr, 46));
这样可以避免从头到尾挨个找数据从而浪费性能
正则表达式:
它代表的是一个规则,通常用来校验数据是否合法或在一段文本中查找满足要求的内容
它的规则是:
1.字符类,只能匹配单个字符:
System.out.println("a".matches("[abc]"));//[abc]只能匹配a,b,c
System.out.println("d".matches("[abcd]"));//[abc]可以扩展
System.out.println("e".matches("[abcd]"));//超出了匹配范围
System.out.println("d".matches("[^abc]"));//[^abc]不能是abc
System.out.println("a".matches("[^abc]"));//false
System.out.println("a".matches("[a-zA-Z]"));//true
System.out.println("6".matches("[a-zA-Z]"));//false
System.out.println("f".matches("[a-z&&[^bcf]]"));//false
System.out.println("g".matches("[a-z&&[^bcf]]"));//a到z,除了bcf
System.out.println("8".matches("[a-zA-Z0-9]"));//true
预定义字符(只能匹配单字符),\d,\D,\s,\S,\w,\W
\\d匹配数字【0,9】
\\s匹配一个空白字符(空格)
\\w匹配一个数字,大小写字母,下划线
System.out.println("牛".matches("."));//.可以匹配任意一个字符
System.out.println("5".matches("\\d"));//\d:0-9
System.out.println("5".matches("\\D"));//\D:^\d
System.out.println(" ".matches("\\s"));//\s:代表一个空白字符
System.out.println("".matches("\\s"));//\s:代表的好像是空格而不是null
System.out.println("5".matches("\\s"));//false
System.out.println("5".matches("\\S"));//\S:^\s
System.out.println("".matches("\\S"));//\S也不能代表一个null
System.out.println(" ".matches("\\S"));//false
System.out.println("b".matches("\\w"));//true
System.out.println("B".matches("\\w"));//true
System.out.println("_".matches("\\w"));//true
System.out.println("5".matches("\\w"));//true
System.out.println(" ".matches("\\W"));//\W:^\w
System.out.println("b".matches("\\W"));//false
3.数量词:? * + {n} {n,} {n,m}
?代表0次或一次
*代表0次或多次
+:表示一或多次
{n}代表正好n次
{n,}代表>=n次
{n,m}代表大于等于n次,小于等于m次
可以和以上两者结合使用
System.out.println("b".matches("\\w?"));//?代表0次或一次
System.out.println("".matches("\\w?"));//
System.out.println("".matches("\\w?"));//代表0次或多次
System.out.println("g".matches("\\w*"));//
System.out.println("ghdbd".matches("\\w*"));//
System.out.println("ghdbd".matches("\\w+"));//+:表示一或多次
System.out.println("g".matches("\\w+"));//
System.out.println("".matches("\\w+"));//
System.out.println("ghdbd".matches("\\w{5}"));//{n}代表正好n次
System.out.println("ghdbd".matches("\\w{3}"));//
System.out.println("g".matches("\\w{3,}"));//{n,}代表>=n次
System.out.println("ghd".matches("\\w{3,}"));//
System.out.println("ghdbd".matches("\\w{3,}"));//
System.out.println("ghdbd".matches("\\w{3,9}"));//{n,m}代表大于等于n次,小于等于m次
System.out.println("ghdbjhftd".matches("\\w{3,6}"));//
System.out.println("a".matches("[abc]?"));
System.out.println("ab".matches("[abc]?"));
4.(?i)忽略大小写,或:|,分组:()
System.out.println("abc".matches("(?i)abc"));//(?i)忽略大小写
System.out.println("Abc".matches("(?i)abc"));//
System.out.println("ABC".matches("(?i)abc"));//
//是……或……
System.out.println("123".matches("\\d{3}|[a-z]{3}"));// 要么是三个数字,要么是三个小写字母
System.out.println("abc".matches("\\d{3}|[a-z]{3}"));// 要么是三个数字,要么是三个小写字母
System.out.println("ABC".matches("\\d{3}|[a-z]{3}"));//
System.out.println("我爱编程666".matches("我爱(编程)+(666)+"));//必须是我爱开头,至少一个编程,至少一个666
System.out.println("我爱编程编程666666".matches("我爱(编程)+(666)+"));
System.out.println("我爱编程编程编程666666666".matches("我爱(编程)+(666)+"));
System.out.println("我爱编程编程编程编程666".matches("我爱(编程)+(666)+"));
System.out.println("我爱编程编程编程编程".matches("我爱(编程)+(666)+"));
replaceAll替换相应字符:
public String replaceAll(String regex,String newStr)按照正则表达式匹配的内容进行替换
String s1="小米wjicbwurhc小明jxchbwuc6489小华kjcneir52jnc";
System.out.println(s1.replaceAll("\\w+", " "));//小米 小明 小华
String s2="我我我我喜欢欢欢欢编编编编编编编编编编编编编编编程程程程程程程程";
System.out.println(s2.replaceAll("(.)\\1+", "$1"));//我喜欢编程
(.)一个组,能匹配一个任意字符 (.)
1,将这个能匹配一个任意字符的组声明组号为1
(.)1+,使这个名为1的组可以匹配任意个相同的任意字符
$1可以取到一组代表的重复的数
split根据正则表达式内容分割字符串
String s3="小米wjicbwurhc小明jxchbwuc6489小华kjcneir52jnc";
String[] name=s3.split("\\w+");
System.out.println(Arrays.toString(name));
异常:
异常的体系建立在Exception上面,然后分为运行时异常(RuntimeException及其子类)和编译时异常
抛出异常:在方法上使用throws关键字,可以将方法内部出现的异常抛出去给调用者处理
捕获异常:try....catch直接捕获出现的异常
自定义异常:
自定义异常
企业自己的问题想通过异常来管理就需要自定义异常了
处理异常的方法:throw,try-catch
保存一个合法的年龄
saveAge(180);
}
public static void saveAge(int age)throws AgeIlegalExceotion{
if (age>0&&age<150){
System.out.println("年龄成功保存:"+age);
} else {
//用一个异常对象去封装这个问题
//throw new AgeIlegalRuntimeExceotion("age is ilegal");
throw new AgeIlegalExceotion("age is ilegal");
}
集合框架:
Collection集合特点:
List系列集合特点:有序,可重复,有索引
有序指的是放进去的元素的顺序和取出来的元素的顺序相等,即存放进去1,2,3,4,5取出来也是1,2,3,4,5
相应的无序指的是取出来可能是2,5,4,1,3
ArrayList和LinekdList特点都是有序,可重复,有索引
set系列集合特点:无序,不重复,无索引
HashSet:无序,不重复,无索引(经过Test试验,好像是降序,又可以有升序,总归不是无序)
LinkedHashSet:有序,不重复,无索引
TreeSet:按照默认大小升序排列,不重复,无索引
Collection集合的常用方法:
Collection<String> c=new ArrayList<>();
//1.添加元素,添加成功返回true
c.add("java1");
c.add("java1");
c.add("java2");
c.add("java2");
//2.清空元素
//c.clear();
System.out.println(c);
//3.判断集合是否为空
System.out.println(c.isEmpty());
//4.获取集合的大小
System.out.println(c.size());
//5.判断集合是否包含某元素
System.out.println(c.contains("java1"));//true
System.out.println(c.contains("Java1"));//false
//6.删除某个元素,若有多个重复元素默认删除前面的第一个,删除成功返回true
System.out.println(c.remove("java1"));
System.out.println(c);
//7.把集合转化为数组
Object[] arr=c.toArray();
System.out.println(Arrays.toString(arr));
//强行转化为String类型的数组
String[] arr2=c.toArray(new String[c.size()]);
//把一个集合中的全部数据倒入到另一个集合中
Collection<String> c1=new ArrayList<>();
c1.add("java1");
c1.add("java1");
Collection<String> c2=new ArrayList<>();
c2.add("java2");
c2.add("java2");
c1.addAll(c2);//把c2中全部数据倒入c1中(c2中的原数据不会删除)
System.out.println(c1);//[java1, java1, java2, java2]
System.out.println(c2);//[java2, java2]
集合的遍历:
1.迭代器
迭代器是用来遍历集合的专用方式,在java中的代表是iterator
通过集合对象调用iterator方法,得到迭代器对象
通过迭代器对象使用方法,
hasNext()询问当前位置是否有元素存在,存在返回true,不存在返回false
next()获取当前位置元素,同时将迭代器对象指向下一个元素处
Collection<String> c=new ArrayList<>();
c.add("大牛");
c.add("小牛");
c.add("老牛");
c.add("大角牛");
System.out.println(c);
//c=[大牛,小牛,老牛,大角牛]
// it
Iterator<String> it = c.iterator();
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
//应该使用循环结合迭代器遍历
while (it.hasNext()){
System.out.println(it.next());
}
2.Lambda表达式:
必须使用forEach才能结合lambda表达式:
Collection<String> c=new ArrayList<>();
c.add("大牛");
c.add("小牛");
c.add("老牛");
c.add("大角牛");
// c.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
// c.forEach((String s) ->{
// System.out.println(s);
// });
// c.forEach(s->System.out.println(s));
c.forEach(System.out::println);
3.增强for循环:
for(元素的数据类型 变量名:数组或集合){ }
Collection<String> c=new ArrayList<>();
c.add("大牛");
c.add("小牛");
c.add("老牛");
c.add("大角牛");
for (String ele : c) {
System.out.println(ele);
}
List集合:
数组的特点:
1.查询速度快:查询通过地址和索引定位,查询任意数据耗时相同
2.删除效率低:可能需要把后面很多数据进行前移
3.添加效率极低:可能需要把后面很多数据后移在添加元素;或者也可能需要进行数组的扩容
通过ArrayList无参构造器创建的集合会在底层创建一个默认长度为0的数组
//且用一个size记录数组的大小
//添加第一个元素时会扩容成一个长度为10的数组
//且将元素添加到第一个位置,然后使s指向它的第二个索引位置‘1’
//然后每次添加数据都会将size指向下一个索引位置
//如果10个元素满了,在添加第11个元素时它就会扩容为原来的1.5倍//地址不会变
//如果已经满10个元素的情况下一次性添加多个元素进来(例如添加11个)在扩容为1.5倍也装不下,这时就会创建一个新数组,长度为10+11并把所有元素装进去(地址不变)
List<String> list1=new ArrayList<>();
List<String> list2=new ArrayList<>();
list1.add("1");
list1.add("1");
list1.add("1");
list1.add("1");
list1.add("1");
list1.add("1");
list1.add("1");
list1.add("1");
list1.add("1");
list1.add("1");
list2.add("2");
list2.add("2");
list2.add("2");
list2.add("2");
list2.add("2");
list2.add("2");
list2.add("2");
list2.add("2");
list2.add("2");
list2.add("2");
list2.add("2");
//System.identityHashCode()方法,返回对象的哈希码,它是根据对象的地址计算出的int值
System.out.println(list1.toString());
System.out.println(System.identityHashCode(list1));
System.out.println(list2);
System.out.println(System.identityHashCode(list2));
list1.addAll(list2);
System.out.println(list1);
System.out.println(System.identityHashCode(list1));
//之所以ArrayList输出数组元素而不是地址,不是ArrayList重写了ToString,而是ArrayList继承了AbstractList,AbstractList又继承了AbstractCollection抽象类重写了toString方法
Linkedlist
LinkedList集合是依据双向链表实现的
//链表中的节点是一个一个独立的对象,在内存中是不连续的,每个节点包含数据之和下一个结点的地址
//链表查询慢,无论查询那个数据都需要从头开始寻找
//链表增删相对快
//单向链表只能从头节点开始从前往后找而但双向链表可以从后往前找,它会在索引之前进行一个判断,判断链表偏后还是偏前
//但对首尾进行增删改查的速度极快
public static void main(String[] args) {
LinkedList<String> queue = new LinkedList<>();
queue.addLast("1");
queue.addLast("2");
queue.addLast("3");
queue.addLast("4");
//创建了队列
System.out.println(queue);
//出队:
queue.removeFirst();
queue.removeFirst();
System.out.println(queue);
addFirst:往双链表的头部增加一个数据
addLast:将指定元素添加到此列表末尾
getFirst:返回此列表第一个元素
getLast:返回此列表最后一个元素
removeFirst:删除并返回第一个数据
removeLast:删除并返回最后一个数据
//数据进入栈模型的过程称之为压/进栈(push)
//数据离开栈模型:弹/出栈(pop)
LinkedList<String> stack = new LinkedList<>();
//先进后出,后进先出
//压栈(push)等同于addLast
stack.push("1");
stack.push("2");
stack.push("3");
stack.push("4");
System.out.println(stack);
//弹栈:(pop)等同于removeLast
stack.pop();
stack.pop();
System.out.println(stack);
1.创建一个ArrayList集合对象(因为List是接口,不能有对象,所以要挑选一个List的实现类)
List<String> list=new ArrayList<>();
list.add("==");
list.add("==");
list.add("==");
list.add("==");
System.out.println(list);
//在某个索引位置插入元素
list.add(2,"牛马");
System.out.println(list);
//根据索引删除元素,返回被删除的元素
System.out.println(list.remove(2));
System.out.println(list);
//返回集合中指定位置处的元素
System.out.println(list.get(3));
//修改指定索引位置处的元素,修改成功后返回原来的数据
System.out.println(list.set(3, "888"));
System.out.println(list);
Set集合:
哈希值就是一个int类型的数值,Java中每个对象都有一个哈希值。
Java中所有对象都可以调用一个Object的hashCode方法,返回对象自己的哈希值
基本上每个对象的哈希值都不一样,但是int类型范围为(-21亿,+21亿),若一次创建过多就会导致哈希值相同称为哈希对撞
同一对象多次由hashCode求哈希值,输出的哈希值相同
public static void main(String[] args) {
String s1 = new String("good");
String s2 = new String("bad");
String s3 = new String("bad");
System.out.println(s1.hashCode());//3178685
System.out.println(s1.hashCode());//3178685
System.out.println(s2.hashCode());//97285
System.out.println(s3.hashCode());//97285
s2和s3内容一样而hashSet没有去重复,所以hashSet没法对内容一样的两个学生对象去重复
内容一样的对象的哈希值相同
哈希值底层是把字符串代入特定公式计算出来的,所以可以依照特定公式去探索
无序,无索引,不重复 //HashSet:无序,无索引,不重复 //linkedHashSet:有序,无索引,不重复 //TreeSet:有序(默认为升序排列),不重复,无索引 Set<Integer> set=new HashSet<>();//经典代码他一般来说只会无序一次,即为只要顺序定下来了,以后就都按照这个顺序了 //Set的常用方法,基本上就是collection提供的,自己几乎没有新增常用功能 //所以使用collection的常用方法就够了
Hashset:
哈希表是一种增删改查数据性能都比较好的数据结构
JDK8以前,哈希表=数组+链表;
1.添加第一个元素时会默认创建一个长度为16的数组,默认加载因子为0.75,数组名为table
2.使用元素的哈希值对数组的长度求余,然后存入余数对应的数组的位置
3.如果当前该位置是null,如果是null直接存入,如果不为null就调用equals方法,如果相等就不存入,不相等就存入
在JDK8之前.直接占老元素位置,老元素挂在下面链表和数组结合就代表每个数组格子都是一个链表,这里的操作其实是***.addFirst("***");
JDK8之后,新元素直接挂老元素下面和上面同理,这里的操作其实是addLast
如果链表上还有其它节点,就和所有节点一一比较
所以查询数据也就是依照哈希码去查询,效率较好
但如果数据过多会导致链表过长,就会使查询性能降低(链表查询性能较差)
一旦16个数组占满了table.length*0.75(加载因子)==12个位置,数组就会扩容,扩容到原数组两倍的大小再重新转移到新数组中
但是就算扩容之后,还是有可能很多数据都到一个数组坑中,导致代码链过长(JDK8以后解决 :1)
JDK8以后,哈希表=数组+链表+红黑树
1//当链表长度超过8且数组长度>=64时,自动将链表转成红黑树
在红黑树中,一个节点能存储多个元素,小哈希值的元素往左边走,大哈希值的元素往右边走
Student st1=new Student("1",12,55.5);
Student st2=new Student("2",12,55.5);
Student st3=new Student("3",12,55.5);
Student st4=new Student("3",12,55.5);
HashSet<Student> hashSet=new HashSet<>();
hashSet.add(st1);
hashSet.add(st2);
hashSet.add(st3);
hashSet.add(st4);
// Iterator<String> iterator=new Iterator<String>() {
// @Override
// public boolean hasNext() {
// return false;
// }
//
// @Override
// public String next() {
// return null;
// }
// };
// System.out.println(hashSet.size());//2
// //证明s2与s3去重复
// Iterator<String> it=hashSet.iterator();
// while (it.hasNext()){
// System.out.println(it.next());//bad
// System.out.println(it.next());//good
// }
//重写equals与hashSet之前无法去重复st3与st4
System.out.println(hashSet.size());//3
Iterator<Student> it=hashSet.iterator();
while (it.hasNext()){
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
}
System.out.println(hashSet);
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Double.compare(height, student.height) == 0 && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, height);
}
Linkedhashset:
有序,不重复,无索引
依然基于哈希表实现
但它的每个元素都额外多了双链表机制,记住每个元素先后位置,
TreeSet:
TreeSet:按照默认大小升序排列,不重复,无索引
基于红黑树实现的排序
Set<Integer> set1=new HashSet<>();
set1.add(6);
set1.add(6);
set1.add(2);
set1.add(7);
System.out.println(set1);
Set<Student> students=new TreeSet<>();
students.add(new Student("A",11,98526.5));
students.add(new Student("B",16,90582.8));
students.add(new Student("C",18,95082.9));
students.add(new Student("D",14,40005.5));
students.add(new Student("E",11,98506.5));
System.out.println(students);//没有E,因为A,E的age一样,Set是不重复的