精华内容
下载资源
问答
  • 思考这个问题的时候,我们不凡复习下不错的分查找,它依赖数组随机访问的特性,其查找时间复杂度为O(log n)。如果我们将元素放入链表中,分查找还好使吗?这就是今天和大家分享的跳表 理解跳表 假设使用单链表...

    阻塞IO

    我们知道在调用某个函数的时候无非就是两种情况,要么马上返回,然后根据返回值进行接下来的业务处理。当在使用阻塞IO的时候,应用程序会被无情的挂起,等待内核完成操作,因为此时的内核可能将CPU时间切换到了其他需要的进程中,在我们的应用程序看来感觉被卡主(阻塞)了。

    非阻塞IO

    当使用非阻塞函数的时候,和阻塞IO类比,内核会立即返回,返回后获得足够的CPU时间继续做其他的事情。

    IO复用模型

    当使用fgets等待标准输入的时候,如果此时套接字有数据但不能读出。IO多路复用意味着可以将标准输入、套接字等都当做IO的一路,任何一路IO有事件发生,都将通知相应的应用程序去处理相应的IO事件,在我们看来就反复同时可以处理多个事情。这就是IO复用

    信号驱动IO

    在信号驱动式 I/O 模型中,应用程序使用套接口进行信号驱动 I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个 SIGIO 信号,可以在信号处理函数中调用 I/O 操作函数处理数据。

    异步IO

    用程序告知内核启动某个操作,并让内核在整个操作(包括将数据从内核拷贝到应用程序的缓冲区)完成后通知应用程序。那么和信号驱动有啥不一样?

    • 讲讲select和epoll的区别?

    这里一样的套路,先说出两者的用途,然后两者的优缺点。

    select的缺点

    • select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件

    • select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调用还是会将这些文件描述符通知进程

    • 内核 / 用户空间内存拷贝问题,select每次都会改变内核中的句柄数据结构集,因而每次select调用时都需要从用户空间向内核空间复制所有的句柄数据结构,产生巨大的开销

    • 单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量

    epoll实现

    epoll在内核中会维护一个红黑树和一个双向链表,红黑树存放通过epoll_ctl方法向epoll对象中添加进来的事件,所以不需要每次调用epoll_wait都全量复制所有的事件结构。双向链表存放就绪的事件,所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法,这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。调用epoll_wait就会直接返回链表中的就绪事件,效率高。

    • select适合少量活跃连接,一般几千。

    • epoll适合大量不太活跃的连接。

    • 乐观锁和悲观锁了解吗?

    这个问题延伸的问题会很多,比如线程安全,CAS原理,优缺点等。

    啥是悲观和乐观,咋们面试的时候不得乐观一些。想给面试来一波官方解释,然后大白话解释一波就差不多了。

    官方:悲观锁是总是假设最坏的情况,每次那数据都认为别人会修改它,所以每次去那数据都要上锁,这样别人去拿这个数据就会阻塞。乐观锁就不一样了,总是觉得一切都是最好的安排,每次拿数据都认为别人不会修改,所以也就不上锁,但是在更新的时候会判断这个期间别人有没有更新这个数据。

    • 什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免?

    缓存穿透

    一般来说,缓存系统会通过key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。这个时候如果一些恶意的请求到来,就会故意查询不存在的key,当某一时刻的请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

    如何避免?

    对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。

    缓存雪崩

    当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。

    如何避免?

    在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

    做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

    不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

    2 redis相关

    如果是后端/服务端面试的同学,怎么说都的去找一本redis书来看看,其出现的概率只有那么大了,切记切记。看看B站问了哪几个问题。

    • redis的淘汰删除策略了解吗?

    能说不了解吗,就算是没有听说过,咋们也可以来一句:“不好意思面试官,这一块还不怎么深入,但是从字面意思来理解巴拉巴拉”,不至于一脸懵逼。下面我们看看redis的缓存策略

    Redis中通过maxmemory参数来设定内存的使用上限,如果Redis所使用内存超过设定的最大值,那么会根据配置文件中的策略选取要删除的key来删除,从而留出新的键值空间。主要的六种淘汰key策略

    1. volatile-lru

    在键空间中设置过期时间,移除哪些最近最少使用的key,占着茅坑不拉屎的key

    1. allkeys-lru

    移除最近最少使用的key

    1. volatile-random

    在键空间中设置过期时间,随机移除一个key

    1. allkeys-random

    随机移除一个key

    1. noeviction

    当内存使用达到阀值的时候,所有引起申请内存的命令会报错;

    ok,现在知道了需要淘汰哪些key,那我们如何去淘汰这些key

    1. 定时删除

    很简单,设置一个闹钟,闹钟响了就删除即可。这种方式对于内存来说还是比较友好,内存不需要啥额外的操作,直接通过定时器就可保证尽快的删除。对于CPU来说就有点麻烦了,如果过期键比较多,那么定时器也就多,这删除操作就会占用太多的CPU资源

    1. 惰性删除

    每次从键空间获取键的时候检查键的过期时间,如果过期了,删除完事。

    1. 定期删除

    每隔一段时间就去数据库检查,删除过期的键

    这种方案是定时删除和惰性删除的中和方法,既通过限制删除操作执行的时长来减少对CPU时间的影响,也能减少内存的浪费。但是难点在于间隔时长需要根据业务情况而定。

    3 mysql

    • mysql中使用的锁有哪些?什么时候使用行锁,什么时候会使用表锁?

    InnoDB中的行锁是通过索引上的索引项实现,主要特点是,只有通过索引条件检索数据,InnoDB才会使用行级锁,否则InnoDB将使用表锁。

    这里注意,在Mysql中,行级锁不是锁记录而是锁索引。索引又分为主键索引和非主键索引两种。如果在一条语句中操作了非主键索引,Mysql会锁定该非主键索引,再锁定相关的主键索引。

    • 了解过间隙锁吗?间隙锁的加锁范围是怎么确定的?

    • 了解B+树吗?B+树什么时候会出现结点分裂?

    这个回答在上一篇的B+树已经详细说了。这里简述一下

    1. 将已满结点进行分裂,将已满节点后M/2节点生成一个新节点,将新节点的第一个元素指向父节点。

    2. 父节点出现已满,将父节点继续分裂。

    3. 一直分裂,如果根节点已满,则需要分类根节点,此时树的高度增加。

    • 事务还没执行完数据库挂了,重启的时候会发生什么?

    • undo日志和redo日志分别是干嘛的?

    redo log重做日志是InnDB存储引擎层的,用来保证事务安全。在事务提交之前,每个修改操作都会记录变更后的数据,保存的是物理日志-数据,防止发生故障的时间点,有脏页未写入磁盘,在重启mysql的时候,根据redo log进行重做从而达到事务的持久性

    undo log回滚日志保存了事务发生之前的数据的一个版本,可以用于回滚,同时也提供多版本并发控制下的读。

    • 简单讲讲数据库的MVCC的实现原理?

    细说太多了,几个大写字母代表啥,这几个大写字母又是如何关联起来完事。细问再深究

    • mysql的binlog日志什么时候会使用?

    首先应该知道binlog是一个二进制文件,记录所有增删改操作,节点之间的复制都会依靠binlog来完成。从底层原理来说,binlog有三个模式

    1. 模式1–row模式

    每一行的数据被修改就会记录在日志中,然后在slave段对相同的数据进行修改。比如说"update xx where id in(1,2,3,4,5)",使用此模式就会记录5条记录

    1. 模式2–statement模式

    修改数据的sql会记录到master的binlog中。slave在复制的时候sql thread会解析成和原来maseter端执行过的相同的sql在此执行

    1. 模式3–mixed模式

    mixed模式即混合模式,Mysql会根据执行的每一条具体sql区分对待记录的日志形式。那么binlog的主从同步流程到底是咋样的

    流程简述:

    Master执行完增删改操作后都会记录binlog日志,当需要同步的时候会主动通知slave节点,slave收到通知后使用IO THREAD主动去master读取binlog写入relay日志(中转日志),然后使 SQL THREAD完成对relay日志的解析然后入库操作,完成同步。

    4 基本数据结构

    • 使用LRU时,如果短时间内会出现大量只会使用一次的数据,可能导致之前大量高频使用的缓存被删除,请问有什么解决办法?

    • 了解过循环链表吗?他的长度怎么计算?

    他的主要特点是链表中的最后一个节点的指针域指向头结点,整个链表形成一个环。****这里循环链表判断链表结束的标志是,判断尾节点是不是指向头结点

    • 哪种数据结构可以支持快速插入,删除,查找等操作?

    思考这个问题的时候,我们不凡复习下不错的二分查找,它依赖数组随机访问的特性,其查找时间复杂度为O(log n)。如果我们将元素放入链表中,二分查找还好使吗?这就是今天和大家分享的跳表

    理解跳表

    假设使用单链表存储n个元素,其中元素有序如下图所示

    从链表中查找一个元素,自然从头开始遍历找到需要查找的元素,此时的时间复杂度为O(n)。那采用什么方法可以提高查询的效率呢?问就是加索引,如何加,我们从这部分数据中抽取几个元素出来作为单独的一个链表,如下图所示]

    假设此时咋们查找元素16,首先一级索引处寻找,当找到元素14的时候,下一个节点的值为18,意味着我们寻找的数在这两个数的中间。此时直接从14节点指针下移到下面的原始链表中,继续遍历,正好下一个元素就是我们寻找的16。好了,我们小结一下,如果从原始链表中寻找元素16,需要遍历比较8次,如果通过索引链表寻找我们只需要5次即可。

    我们继续查找元素16,此时比较次数变为4次。这样看来,加一层索引查找的次数就变少,如果有n个元素到底有多少索引?

    假设我们按照每两个结点就抽出一个结点作为上一层的索引节点,第一层所以节点个数n/2,第二层为n/4,第x级索引的结点个数是第x-1级索引的结点个数的1/2,那第x级索引结点的个数就是n/(2x)。假设索引有y级,我们可以得到n/(2y)=2,从而求得y=log2n-1。

    这么多索引是不是就很浪费内存嘞?

    假设原始链表大小为n,那第一级索引大约有 n/2 个结点,第二级索引大约有 n/4 个结点,以此类推,每上升一级就减少一半,直到剩下 2 个结点。如果我们把每层索引的结点数写出来,就是一个等比数列。这几级索引的结点总和就是 n/2+n/4+n/8…+8+4+2=n-2 。所以,跳表的空间复杂度是 O(n) 。那还能不能降低一些呢。机智的你应该就考虑到假设每三个结点抽取一个节点作为索引链表的节点。

    跳表与二叉查找树

    两者其查找的时间复杂度均为O(logn) ,那跳表还有哪些优势?

    先看二叉查找树,

    这种结构会导致二叉查找树的查找效率变为 O(n),。

    跳表与红黑树

    说实话,红黑树确实比较复杂,面试的时候让你写红黑树,你就给他大嘴巴子?

    红黑树需要通过左右旋的方式去维持树大小平衡。而跳表是通过随机函数来维护前面提到的 “ 平衡性 ” 。当我们往跳表中插入数据的时候,我们可以选择同时将这个数据插入到部分索引层中。如何选择加入哪些索引层呢?
    我们通过一个随机函数,来决定将这个结点插入到哪几级索引中,比如随机函数生成了值 K ,那我们就将这个结点添加到第一级到第 K 级这 K 级索引中。当我们往跳表中插入数据的时候,我们可以选择同时将这个数据插入到部分索引层中。

    小结

    Redis中的有序集合采用了跳表的方式来实现,其实还采用了散列表等数据结构进行融合。它在插入,删除等都有比较快的速度,虽然红黑树也可以做到,但是红黑树对于按照区间查找数据这个操作,跳表可以做到 O(logn) 的时间复杂度定位区间的起点,然后在原始链表中顺序往后遍历就可以了

    5 总结

    请记下以下几点:

    • 公司招你去是干活了,不会因为你怎么怎么的而降低对你的要求标准。

    • 工具上面写代码和手撕代码完全不一样。

    • 珍惜每一次面试机会并学会复盘。

    • 对于应届生主要考察的还是计算机基础知识的掌握,项目要求没有那么高,是自己做的就使劲抠细节,做测试,只有这样,才知道会遇到什么问题,遇到什么难点,如何解决的。从而可以侃侃而谈了。

    • 非科班也不要怕,怕了你就输了!一定要多尝试。

    最后

    再免费分享一波我的JAVA架构专题面试真题+解析+JAVA学习书籍:戳这里免费领取

    image.png

    基础知识的掌握,项目要求没有那么高,是自己做的就使劲抠细节,做测试,只有这样,才知道会遇到什么问题,遇到什么难点,如何解决的。从而可以侃侃而谈了。

    • 非科班也不要怕,怕了你就输了!一定要多尝试。

    最后

    再免费分享一波我的JAVA架构专题面试真题+解析+JAVA学习书籍:戳这里免费领取

    [外链图片转存中…(img-G1v9fk8E-1627356737175)]

    展开全文
  • 1.Java语言的特点(1)Java语言是一种面向对象的编程语言(2)简单、高效、稳定、安全性高(3)Java语言是一种与平台无关的编程语言,因为其自身提供程序运行的解 释环境(4)支持多线程处理(5)Java语言是一种面向网络的高级...

    1.Java语言的特点

    (1)Java语言是一种面向对象的编程语言

    (2)简单、高效、稳定、安全性高

    (3)Java语言是一种与平台无关的编程语言,因为其自身提供程序运行的解 释环境

    (4)支持多线程处理

    (5)Java语言是一种面向网络的高级语言

    2.ava两种核心机制

    Java虚拟机JVM(Java Virtual Machine)

    垃圾回收机制(Garbage Collection)

    3.JDK是整个Java的核心,其中 包括Java的运行环境(Java Runtime Envirnment),一堆Java工具和Java基础类库(rt.jar)

    4.JDK的基本组件包括:

    (1) .javac — 编译器,将源程序转为字节码;

    (2) .jar — 打包工具,将相关的类文件打包成一个文件;

    (3) .javadoc — 文档生成器,从源码注释中提取文档;

    (4) .jdb — debugger,查错工具;

    (5) .java — 运行编译后的Java程序(后缀为.class)使用dir命令可查看D盘中的所有文件运行javac HelloWorld.java命令java HelloWorld

    第二章 JAVA语法

    1.类名、变量名以及方法名都被称为标识符

    2.关于Java标识符,有以下几点需要注意:

    (1)所有的标识符都应该以字母(A-Z或者a-z),美元符($)、或者下划 线(_)开始

    (2) 首字符之后可以是字母(A-Z或者a-z),美元符($)、下划线(_)或 数字的任何字符组合

    (3)关键字不能用作标识符

    (4)标识符是大小写敏感的

    3.所有java关键字都是小写英文 goto和const虽然从未被使用,但是作为java关键字保留起来。

    4.Java语言中的数据类型分为两大类:(1)基本数据类型: 共有4类8种,分别是整数型(byte型、short型、int型、long型)

    浮点型(float型、double型)、字符型(char型)和布尔型(boolean型)。(2)引用数据类型,又称为复合数据类型: 包括数组(array)、类(class)、接口(interface)和字符串(String)。

    注意:String 不是基本数据类型,是引用数据类型,它是Java提供的一个类

    5.数据在进行类型转换时遵循以下几个原则:

    (1) boolean(布尔)型的数据不能与其他数据类型进行相互转换;

    (2) 自动类型转换发生在容器小的类型转换为容器大的类型的情况下;

    (3) 数据类型按容器大小排序为: Byte,short,char->int->long->float->double

    (4) Byte,short,char之间不会相互转换,他们三者在计算时首先会转换成int类型

    (5) 容器大的类型转换成容器小的类型时,要加上强制转换符,但可能造成精度降低或者溢出, 使用时要格外小心;

    (6)多种数据类型混合运算时,系统首先将所有的数据转换成容器最大的那个类型,然后再进行计算

    6. Java变量的分类

    (1)按被声明的位置划分:

    局部变量:方法或语句块内部定义的变量

    成员变量:方法外部,类的内部定义的变量

    注意:类外面(与类对应的大括号外面)不能有变量的声明

    (2)按所属的数据类型划分: 基本数据类型变量 引用数据类型变量

    7. 使用变量的步骤

    第一步:声明变量,根据数据类型在内存申请空间

    第二步:赋值,将数据存储至对应的内存空间

    第三步:使用变量,获取数据值 数据类型 变量名;

    变量名 = 数值; 数据类型 变量名 = 数值;

    注:第一步和第二步可以合并

    int amount; money = 200; 等价于int money = 200;8.

    常量:final = final double PI=3.149.

    单目运算符包括! ~ ++ --,优先级别高

    (1)优先级别最低的是赋值运算符

    (2)可以通过()控制表达式的运算顺序,()优先级最高

    (3)从右向左结合性的只有赋值运算符、三目运算符和单目运算符

    (4)算术运算符 > 关系运算符 > 逻辑运算符

    三目运算符:三元运算符 int min; min = 5 < 7 ? 5 : 7;

    //条件?表达式1:表达式2,。判断条件,为真则执行表达式1,为假则执行表达式2.

    System.out.println(min); min = 10 < 7 ? 10 : 7;

    System.out.println(min);

    10.运算符的优先级 :最高的优先级:( ) 最低的优先级:=

    优先级顺序!> 算术运算符 > 比较运算符 > && > ||

    11. while(): 特点:先判断,再执行 do while(): 特点:先执行,再判断

    12. 区别1:语法

    区别2:执行顺序

    (1)while 循环: 先判断,再执行(初始情况不满足循环条件时,一次都不会执行)

    (2)do-while循环:先执行,再判断((初始情况不满足循环条件时,至少执行一次))

    (3)for循环: 先判断,再执行

    区别3:适用情况 (1)循环次数确定的情况,通常选用for循环

    (2)循环次数不确定的情况,通常选用while和do-while循环 continue:作用是略过当次循环中剩下的语句

    13,比较switch 和多重if选择结构: 相同点:都是用来处理多分支条件的结构 不同点:switch选择结构:只能处理等值条件判断的情况 。

    多重if选择结构:适合某个变量处于某个连续区间时的情况

    14, 流程 控制语句- break:改变程序控制流:用于do-while、while、for循环中。需要在某种条件出现时,强行终止结束循环,而不是等到循环条件为false时终止 。(可用于switch结构和循环结构中),(终止某个循环,程序跳转到循环块外的下一条语句)

    continue :只能在循环中使用,continue只是中止本次循环,接着开始下一次循环

    第三章 数组

    1. 数组是Java语言中常见的一种引用数据类型,它是具有相同数据类型的一组数据的集

    2. 数组基本要素(1) 标识符:数组的名称,用于区分不同的数组;

    (2) 数组元素:向数组中存放的数据;

    (3) 元素下标:对数组元素进行编号,从0开始,数组中的每个元素都可以通过下标来访问;

    (4) 元素类型:数组元素的数据类型。

    3. 数组都具有以下主要特征

    (1) 数组是由若干个相同数据类型的元素所组成的集合;

    (2) 数组中的元素在内存中是按照一定的顺序进行存放的,实际上数组是一个简单的线性序列;

    (3) 数组元素是通过数组名与其所在数据的索引(即下标)所确定的,数组的索引从0开始;

    (4) 数组是具有一定长度的,数组的长度表示该数组能够存储数组元素的个数;

    (5) 正是因为数组是固定长度的集合,所以Java语言中对数组的初始化、赋值及使用的过程中都会进行边界判定的校验。

    Out of bound exception “”引上的都是String类型

    4. 一维数组声明的语法格式如下:

    (1)数据类型[] 数组名;(2)数据类型 数组名[];

    例:int[] a; int a[]

    (3)数组元素类型[] 数组名 = new 数组元素类型[数组长度];

    例:String[] week = new String[7];int[] B = new int[5];

    5. 使用数组四步走:

    1、定义数组 int[ ] a;

    2、分配空间 a = new int[7];

    3、赋值 a [0] = 2;

    4、数据处理 a [0] = a[0] * 4;

    6,定义数组并赋值的两种方式:

    静态赋值 int[ ] array = {25, 54, 80, 65, 33}; int[ ] array = new int[ ]{25, 54, 80, 65, 33};

    动态赋值 Scanner input = new Scanner(http://System.in); int arrary[] = new int[6]; for(int i=0; i

    7,常用的数组操作 :

    (1) 遍历 :a,一维数组的 遍历- - for循环

    int[] a = new

    int[]{1,3,5,7,9};

    for(int i = 0;i

    { System.out.println("a["+i+"]="+a[i]); }

    b, 一维数组的遍历- - 增强型 for 循环: for(声明语句 : 表达式) { //代码句子 }

    如: int[] a = new

    int[]{1,3,5,7,9};

    for(int i : a)

    {System.out.println(i); }

    c,二维数组的遍历

    int[][] arr = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

    System.out.println(" 遍历二维数组:");

    for (int i = 0; i < arr.length; i++)

    { // 第一层循环 for (int j = 0; j < arr[i].length; j++) { // 第二层循环 System.out.print(arr[i][j] + " ");

    sum += arr[i][j]; } sytem.out.println(); }

    (2) 排序 :(从小到大排序)

    快速排序; int[] a = new int[]{2,4,3,6,2,8,1}; Arrays.sort(a); //a数组名 for(int i:a){System.out.print(i+" t");}

    冒泡排序;运用数组的遍历 int[] a = new int[]{3,43,25,16,31,57,8}; for (int i = 0; i < a.length; i++) { for (int j = i+1; j < a.length; j++) { if(a[i]>a[j]){ int k = a[i]; a[i]=a[j]; a[j]=k; } }

    选择排序;将数组的第一个数作为最大值或最小值,然后通过循环比较, 达到数组排序的目的。 for (int i = 0; i < a.length- - 1; i ++) { int min = i i; ; for (int j = i+1; j < a.length ; j++ ) { if (a[min]>a[j]){ min=j; } } if(min!=i i ){ int temp = a[i i ]; a[ i ] = a[min]; a[min] = temp; } }

    插入排序:插入排序是选择一个数组中的数据,通过不断的插入比较最后进行排序。 for ( int i = 1; i < a.length ; i ++) { { for ( int j = i i ; j > 0; j -- ) { if (a[j] < a[j - - 1]) { int temp = a[j - - 1]; a[j - - 1] = a[j]; a[j ] = temp; } else break; } }

    (3) 复制 :a

    ,运用循环语句逐个复制数组中的元素;(简单的for循环或增强for循环)

    b ,使用System类中的静态方法arrayCopy()。

    c,使用Arrays类的静态方法copyOf()方法和copyOfRange()方法来实现数组的复制;

    c条本质也是调用了System类的静态方法arraycopy()

    (4)替换:fill(数组名,替换的值); Arrays.fill(a, 5);

    fill(数组名,起始下标,结束下标,替换的值); Arrays.fill(a,1,3,5);

    各位初学者小伙伴们,如果需要Java入门的详细教学视频可以私信我,或者留下评论,我会分享给各位!最后再恳求各位点个赞!

    74c78d8384315c63a26a8b679d0f93cf.png

    原文链接: https://bbs.csdn.net/topics/392519223

    展开全文
  • 在编程语言中矩阵可以用一个二维数组进行模拟,如下图所示:矩阵和二维数组大多数高级语言中,都支持二维数组,用二维数组来实现矩阵是非常容易的,编程语言更多的是用来模拟矩阵的加减操作以及移动操作。...

    1. 矩阵

    在数学中,矩阵是一个矩形阵列排列的复数和实数集合。是高等代数中常见的工具。在计算机科学中,一些数值分析、动画制作、游戏等方面就会用到矩阵。在编程语言中矩阵可以用一个二维数组进行模拟,如下图所示:

    af28cba09571646b81a91d448c8d55e0.png

    矩阵和二维数组

    大多数高级语言中,都支持二维数组,用二维数组来实现矩阵是非常容易的,编程语言更多的是用来模拟矩阵的加减操作以及移动操作。

    2. 矩阵的操作

    在一些游戏设计中,经常会用到矩阵进行操作,例如加减操作和旋转操作就是使用频率较多的操作。下面我们列举几个常见的矩阵操作:

    2.1 矩阵相加

    一般情况下,两个矩阵相加,需要两个矩阵拥有相同的大小(如下图所示):

    fcdbcae03727f724a5be84625f11a773.png

    矩阵相加

    如果在计算机中进行模拟矩阵的相加,相当于两个同等规模的二维数组相加,并进行同行同列的元素相加。相加后得到的矩阵依然是一个同等规模的矩阵数组。这个操作在编程语言中操作是非常简单的。

    2.2 矩阵错位相加

    矩阵错位相加时,可以让两个不同规模的矩阵相加,这个模式的相加方式,只对两个矩阵重合的部分进行相加(如下图所示)。

    4bbbb2969e02ab21e487287b6001ad80.png

    矩阵错位相加

    2.3 矩阵旋转

    矩阵旋转需要矩阵是一个规则矩阵(矩阵的长和宽相等,否则旋时丢失数据),矩阵的旋转转角为90度的倍数(如下图所示)。

    c79d9f8610f7701efde07a7a424580a2.png

    矩阵旋转

    矩阵的旋转在游戏中的应用非常广泛,例如我们熟悉的俄罗斯方块中,就会用到矩阵的旋转。

    3. Java程序模拟矩阵

    在Java中,模拟矩阵需要创建一个行列平衡的二维数组(要求每行的元素个数都一样)。如果要进行旋转,必须要求二维数组的行列一致。我们可以定义类Matrix进行矩阵的模拟,它需要包含一个二维数组,以及表示二维数组的行列整型值。Matrix类的声明及构造方法如下所示:

    744e359e3867c545a6bc3f9aee7904a8.png

    3.1矩阵添加元素操作

    为了给矩阵增加数据添加的入口,我们可以在Matrix类中增加矩阵添加数据的操作(整体矩阵随机添加元素、按行和列添加元素),以及一个打印矩阵的方法,这些方法如下所示:

    8f9bf9d11c01cf9af5f08b5e043bc40d.png

    这里要注意方法setforEach中的Function参数,它是一个函数式接口。它的目的在于让开发者自己定义数据的计算规则。泛型V是函数方法apply中的参数,泛型R是apply方法的返回类型。在setforEach方法内,我们将矩阵元素作为参数传递到apply方法中。在方法setRandomMatr中,我们就利用setforEach进行了随机数添加,但是我们没有使用矩阵中的元素。

    这几个方法在添加到Matrix类中后,我们可以在main方法中进行一些测试(测试内容和结果如下所示):

    b06b5bb142703477ed6b74e87901b7c8.png
    69ba86b95abe892baf73530d4e757032.png

    3.2 矩阵相加操作

    矩阵的第二个功能是完成矩阵的相加和错位相加,这两种相加方式可以通过参数来完成。进行错位相加时,要判断数组中的元素是否出界。在Matrix类中,添加add方法用于进行矩阵的相加和错位相加(如下所示)。

    aa335bf7182f57f129021e8eeb68fbf6.png

    进行矩阵相加时,要有一个基础矩阵做为被加矩阵,被加矩阵默认为当前矩阵。进行矩阵相加时,允许偏移量小于0。下面为矩阵相加功能的测试结果:

    7da97aa790c4e221a206fcec1c77ccd1.png
    11a83559913f28401ad538cb9f3d2327.png

    3.3 矩阵旋转操作

    矩阵的旋转是矩阵操作中稍微复杂一点的操作,如果找到规律后,矩阵的旋转也不是难事。矩阵旋转的前提条件是必须保证矩阵是一个长宽相等的正方形矩阵。其次旋转时只能以90°的倍数进行旋转(向左或向右):

    f805cfcd3f0895b588c64807b330dfb2.png

    矩阵旋转规则

    上图中左侧为顺时针旋转90°时的算法。每一种颜色表示要旋转的圈数。当矩阵的长度为基数或者偶数的时候,旋转的圈数都可以记为n/2(n为矩阵长度)。

    以上图左侧为例,当矩阵旋转时,每旋转一个矩阵元素时,要同时涉及到四个矩阵元素同时旋转进行数据交换(左图各颜色线条所标识),这四个元素之间的关系如下图所示:

    71ccdec517b10cdbc3e7ce87b07b816c.png

    矩阵旋转中元素之间的关系

    在旋转操作上,我们实际上只要操作上图6中白色标记的元素,即可完成所有元素的旋转。如果在双层循环中完成旋转操作,外层只要遍历矩阵的深度的一半,即i

    1f98b0d633c7051562c50a025b03f53f.png

    如果旋转的角度不是90°的整数倍,我们按照除以90取整的方式进行,并计算出旋转的角度是90,180还是270度。如果角度为负数,则说明是相反方向旋转。在leftRotate和rightRotate中都满足了上述两种情景的判断。上述方法测试情况如下:

    a1643d73d7ed509855b58e337460b977.png
    883e6e81f4e57060cef231f926c656d5.png

    如果我们想设计一个俄罗斯方块的游戏,就可以用到数组实现矩阵的一些功能。在以后的内容中,我们会进行讲述。

    展开全文
  • 作者:CSDN博主沉默王两年前,我甚至写过一篇文章,吐槽数组Java 中挺鸡肋的,因为有 List 谁用数组啊,现在想想那时候的自己好幼稚,好可笑。因为我只看到了表面现象,实际上呢,List 的内部仍然是通过数组...

    作者:CSDN博主沉默王二

    两年前,我甚至写过一篇文章,吐槽数组在 Java 中挺鸡肋的,因为有 List 谁用数组啊,现在想想那时候的自己好幼稚,好可笑。因为我只看到了表面现象,实际上呢,List 的内部仍然是通过数组实现的,比如说 ArrayList,在它的源码里可以看到下面这些内容:

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access
    
    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

    数组在 Java 中,必须算是核心,神一般的存在。

    01、什么是数组

    按照 Javadoc 给出的解释,数组是一个对象,它包含了一组固定数量的元素,并且这些元素的类型是相同的。数组会按照索引的方式将元素放在指定的位置上,意味着我们可以通过索引来访问到这些元素。在 Java 中,索引是从 0 开始的。

    我们可以将数组理解为一个个整齐排列的单元格,每个单元格里面存放着一个元素。

    数组元素的类型可以是基本数据类型(比如说 int、double),也可以是引用数据类型(比如说 String),包括自定义类型的对象。

    了解了数组的定义后,让我们来深入地研究一下数组的用法。

    在 Java 中,数组的声明方式有两种。

    先来看第一种:

    int[] anArray;

    再来看第二种:

    int anOtherArray[];

    不同之处就在于中括号的位置,是紧跟类型,还是放在变量名的后面。前者比后者的使用频率更高一些。

    接下来就该看看怎么初始化数组了,同样有多种方式可以初始化数组,比如说最常见的是:

    int[] anArray = new int[10];

    使用了 new 关键字,对吧?这就意味着数组的确是一个对象。然后,在方括号中指定了数组的长度,这是必须的。

    这时候,数组中的每个元素都会被初始化为默认值,int 类型的就为 0,Object 类型的就为 null。

    另外,还可以使用大括号的方式,直接初始化数组中的元素:

    int anOtherArray[] = new int[] {1, 2, 3, 4, 5};

    这时候,数组的元素分别是 1、2、3、4、5,索引依次是 0、1、2、3、4.

    02、访问数组

    前面提到过,可以通过索引来访问数组的元素,就像下面这样:

    anArray[0] = 10;
    System.out.println(anArray[0]);

    通过数组的变量名,加上中括号,加上元素的索引,就可以访问到数组,通过“=”操作符进行赋值。

    如果索引的值超出了数组的界限,就会抛出 ArrayIndexOutOfBoundException,关于这方面的知识,我之前特意写过一篇文章,如果你感兴趣的话,可以跳转过去看看。

    为什么会发生ArrayIndexOutOfBoundsException

    我觉得原因挺有意思的。

    既然数组的索引是从 0 开始,那就是到数组的 length - 1 结束,不要使用超出这个范围内的索引访问数组,就不会抛出数组越界的异常了。

    03、遍历数组

    当数组的元素非常多的时候,逐个访问数组就太辛苦了,所以需要通过遍历的方式。

    第一种,使用 for 循环:

    int anOtherArray[] = new int[] {1, 2, 3, 4, 5};
    for (int i = 0; i < anOtherArray.length; i++) {
        System.out.println(anOtherArray[i]);
    }

    通过 length 属性获取到数组的长度,然后索引从 0 开始遍历,就得到了数组的所有元素。

    第二种,使用 for-each 循环:

    for (int element : anOtherArray) {
        System.out.println(element);
    }

    如果不需要关心索引的话(意味着不需要修改数组的某个元素),使用 for-each 遍历更简洁一些。当然,也可以使用 while 和 do-while 循环。

    04、可变参数

    可变参数用于将任意数量的参数传递给方法:

    void varargsMethod(String... varargs) {}

    varargsMethod() 方法可以传递任意数量的字符串参数,可以是 0 个或者 N 个,本质上,可变参数就是通过数组实现的,为了证明这一点,我们可以通过 jad 反编译一下字节码:

    public class VarargsDemo
    {
    
        public VarargsDemo()
        {
        }
    
        transient void varargsMethod(String as[])
        {
        }
    }

    所以我们其实可以直接将数组作为参数传递给可变参数的方法:

    VarargsDemo demo = new VarargsDemo();
    String[] anArray = new String[] {"沉默王二", "一枚有趣的程序员"};
    demo.varargsMethod(anArray);

    也可以直接传递多个字符串,通过逗号隔开的方式:

    demo.varargsMethod("沉默王二", "一枚有趣的程序员");

    05、把数组转成 List

    List 封装了很多常用的方法,方便我们对集合进行一些操作,而如果直接操作数组的话,多有不便,因此有时候我们需要把数组转成 List。

    最原始的方式,就是通过遍历数组的方式,一个个将数组添加到 List 中。

    int[] anArray = new int[] {1, 2, 3, 4, 5};
    
    List<Integer> aList = new ArrayList<>();
    for (int element : anArray) {
        aList.add(element);
    }

    更优雅的方式是通过 Arrays 类的 asList() 方法:

    List<Integer> aList = Arrays.asList(anArray);

    但需要注意的是,该方法返回的 ArrayList 并不是 java.util.ArrayList,它其实是 Arrays 类的一个内部类:

    private static class ArrayList<E> extends AbstractList<E>
            implements RandomAccess, java.io.Serializable{}

    如果需要添加元素或者删除元素的话,最好把它转成 java.util.ArrayList。

    new ArrayList<>(Arrays.asList(anArray));

    06、把数组转成 Stream

    Java 8 新增了 Stream 流的概念,这就意味着我们也可以将数组转成 Stream 进行操作,而不是 List。

    String[] anArray = new String[] {"沉默王二", "一枚有趣的程序员", "好好珍重他"};
    Stream<String> aStream = Arrays.Stream(anArray);

    也可以直接对数组的元素进行剪辑,通过指定索引的方式:

    Stream<String> anotherStream = Arrays.Stream(anArray, 1, 3);

    结果包含"一枚有趣的程序员"和"好好珍重他",1 这个索引位置包括,3 这个索引位置不包括。

    07、数组排序

    Arrays 类提供了一个 sort() 方法,可以对数组进行排序。

    基本数据类型按照升序排列

    实现了 Comparable 接口的对象按照 compareTo() 的排序

    来看第一个例子:

    int[] anArray = new int[] {5, 2, 1, 4, 8};
    Arrays.sort(anArray);

    排序后的结果如下所示:

    [1, 2, 4, 5, 8]

    来看第二个例子:

    String[] yetAnotherArray = new String[] {"A", "E", "Z", "B", "C"};
    Arrays.sort(yetAnotherArray, 1, 3,
                    Comparator.comparing(String::toString).reversed());

    只对 1-3 位置上的元素进行反序,所以结果如下所示:

    [A, Z, E, B, C]

    08、数组搜索

    有时候,我们需要从数组中查找某个具体的元素,最直接的方式就是通过遍历的方式:

    int[] anArray = new int[] {5, 2, 1, 4, 8};
    for (int i = 0; i < anArray.length; i++) {
        if (anArray[i] == 4) {
            System.out.println("找到了 " + i);
            break;
        }
    }

    上例中从数组中查询元素 4,找到后通过 break 关键字退出循环。

    如果数组提前进行了排序,就可以使用二分查找法,这样效率就会更高一些。Arrays.binarySearch() 方法可供我们使用,它需要传递一个数组,和要查找的元素。

    int[] anArray = new int[] {1, 2, 3, 4, 5};
    int index = Arrays.binarySearch(anArray, 4);

    09、总结

    除了一维数组,还有二维数组,但说实话,二维数组不太常用,这里就不再介绍了,感兴趣的话,可以尝试打印以下杨辉三角。

    这篇文章,我们介绍了 Java 数组的基本用法和一些高级用法,我想小伙伴们应该已经完全掌握了。

    ————————————————

    版权声明:本文为CSDN博主「沉默王二」的原创文章

    最后,看完这篇内容后,相信以下三件事,也会给你带来帮助:

    1、点赞,让更多小伙伴能看到这篇内容,你的点赞是我持续创作的动力

    2、提升自己,CSDN放出了大量免费课送给正在阅读的你,包含Python、Java、人工智能、算法、机器学习等热门领域,只要你想学,这里都有!

    戳下方链接查看:

    IT培训 机构_课程_视频教程-CSDN学院​edu.csdn.net

    3、关注【CSDN学院】公众号,更多面试题、面试技巧等优质内容等你来撩!

    展开全文
  • 一行代码解决二维数组添加元素问题 在今天用二维数组的时候,想往里面添加一条信息,看网上基本都是通过双重集合实现的,于是我这里用了一下map做转换实现的 其实就是先把二维数组转换成map,然后通过map添加一条...
  • Java二维数组

    千次阅读 2021-02-14 11:42:42
    Java二维数组中,数据存储在行和列中,我们可以使用行索引和列索引(例如Excel File)访问记录。 如果数据是线性的,则可以使用一维数组。但是,要处理多层数据,我们必须使用多维数组。Java中的二维数组是...
  • 判断一维数组是否存在二维数组里 不存在就push到二维数组中形成二维数组,存在则不用管
  • 使用java8中stream,遍历二维数组的情况,使用下标,并且往每行添加数据 Object[][] newData = (Object[][]) IntStream.range(1, data.length).mapToObj(i -> {//从第1行-最后一行 List list = new ArrayList...
  • Java二维数组实现学生成绩存储

    千次阅读 2020-07-08 11:02:59
    import java.util.Scanner; public class ScoreReceiveAndOutput { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int scoreArr[][] = new int[2][3]; // 循环接收2个...
  • Java二维数组和动态数组ArrayList 类

    千次阅读 2020-09-18 00:32:30
    Java二维数组 Java 语言中提供的数组是用来存储固定大小的同类型元素。 1.二维数组初始化和声明 数组变量的声明,和创建数组可以用一条语句完成,如下所示: int a[][] = new int[2][3]; int[][] arr = {{1,2,3...
  • 一个二维数组要想得到这种效果,需要添加行下标和列下标 题目为:定义一个20*5的二维数组,用来存储某班级20位学员的5门课的成绩;这5门课 按存储顺序依次为:core C++,coreJava,Servlet,JSP和EJB。 由此可知,...
  • /** 数组分类 1. 一维数组 1.1 一维数组的定义和初始化 1.2 对一维数组的操作, 遍历,添加,插入,修改,删除,排序,查找 2. 二维数组 2.1 二维数组的定义和初始化 2.2 二维数组的遍历 */ public clas...
  • Java 二维数组迷宫游戏

    千次阅读 2018-01-06 15:20:51
    二维数组迷宫 分析: 1.打印地图 --- 二维数组的遍历 初始角标array[1][1] 结束角标array[1][9] 2.如何让'0'走起来 通过修改横纵角标达到让'0'走起来的目的 如何碰到墙'#'不交换移动 可以先把要走位置的元素...
  • Java二维数组实现学生管理系统 一、预备知识 1.二维数组的创建 1) DataType [][] name=new DataType[10][]; 2) DataType [][] name=new DataType[9][5]; 3) DataType [][] name= {}; 2.二维数组的初始化 1) ...
  • 数组类型【】 数组名 = new 数据类型【】{元素1,元素2,元素3…}; 02简化格式: 数组类型【】 数组名 = {元素1,元素2,元素3…}; 打印数组名: public static void main(String[] args) { //创建一个数组 double ...
  • JAVA中的二维数组

    2010-02-08 16:30:39
    for(int row[] :arr) //此时不难看出,二维数组可以看作是每个元素都是一个一维数组的一维数组 { for(int item: row) { System.out.print(item+ " "); } System.out.println(); } 补充: //...
  • Java二维数组与构造方法

    千次阅读 2019-04-18 08:49:33
    1、二维数组 比较一维数组:一维数组对于一维数组的时候,如果元素是简单数据类型,我们直接将数据本身放入数组,如果元素是引用数据类型,我们是将对象的地址放入数组。 创建: 赋值/取值:————下标 遍历:...
  • Java二维数组Java 语言中提供的数组是用来存储固定大小的同类型元素。1.二维数组初始化和声明数组变量的声明,和创建数组可以用一条语句完成,如下所示:int a[][] = new int[2][3];1int[][] arr = {{1,2,3},{4,5,6}...
  • 数组的数组---二维数组的每一个元素是一个一维数组 定义格式 数据类型[][] 数组名 = new 数据类型[二维数组的长度/包含的一维数组的个数][每个一维数组的长度]; int[][] arr = new int[3][5];---定义了一个整型的...
  • 循环二维数组并给其添加元素

    千次阅读 2018-01-19 14:58:25
    array:3 [ 0 => array:22 [ "id" => 320 "name" => '小名' ] 1 => array:22 [ "id" => 321 "name" => '小红' ] 2 => & array:22 [ "id" => 323 "name" => '小兰' ... ]上述数组 $st
  • 本文介绍了关于Java中二维数组的一点见识,除此之外还介绍了Java中二维数组声明的不同方式。
  • java 使用 lamba 表达式处理二维数组

    千次阅读 2017-12-05 23:09:39
    编写数学优化算法时遇到一个问题:如何将一个二维数组满足一定条件的行,筛选出来并组成一个新的数组。 传统思路: 编写一个专门的方法,这个方法返回满足条件的新数组,但数组必须首选确定满足条件的行数,有点...
  • Java基础——Arrays工具类、二维数组

    千次阅读 2018-08-09 13:04:27
    Arrays工具类、二维数组 1.Arrays工具类 2.方法传参和返回值 3.可变参数 4.二维数组 1.1 二维数组的概念 1.2 二维数组的定义 1.3 数组的初始化 1.4 二维数组的访问 5.调试和文档注释 第一节 Arrays工具类 ...
  • Arrays工具类存在于java.util包下,所以使用的第一步就是导包:import java.util.Arrays; 注意1:如果在同一个Java文件中同时使用Scanner和Arrays,则可以向如下方式导包: import java.util.Scanner; ...
  • 如果有三组数:['A1','A2','A3']、['B1','B2']、['C1','C2'],从每一组里抽出一个数并进行组合,且每种情况不同,一共有3*2*2=12种情况,这是很熟悉的数学题,如果用代码实现,应该如何做呢?其实我试过手写了一下...
  • 1.定义 稀疏数组可以看做是普通数组的压缩,但是这里说的普通数组是值无效数据量远大于有效数据量的数组,关于稀疏数组的运用有五子棋盘,地图等.. *当一个数组中大部分元素为0,或者为同一个值的数组时,可以...
  • Java 实例 - 向数组添加元素

    千次阅读 2019-09-16 09:43:58
    下面数据插入元素方法主要思路是通过,System.arraycopy()复制数组实现。 @Test public void test4(){ int[] array = new int[]{2, 5, -1}; Arrays.sort(array); System.out.println("排序后...
  • (自恋.jpg)迟到不等于缺席,【零基础】小声bb Java系列将会一直更新。这不,我又回来了~这一系列开始的初衷我不会忘掉,曾经在第一讲里立过的flag也绝不会断。(毕竟,,,话已经说出去了,这么快hit face(da lian...
  • 利用Java二维数组实现银行管理系统,包括注册账户,存钱,取钱,转账等功能。 其中包含动态数组,循环嵌套,数组遍历,添加元素,查找元素等方法; 将整个程序分为两个菜单,一级菜单为注册和登录,二级菜单为存钱,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,378
精华内容 10,151
关键字:

java二维数组添加元素

java 订阅