用Java代码实现hadoop命令
来源:互联网 发布:python 教务系统 编辑:程序博客网 时间:2024/06/04 18:55
一. 获取文件系统实例
通过FileSystem的get()或newInstance()方法获取文件系统的实例。
get()和newInstance()方法分别有3个重载方法:
//返回默认文件系统,core-site.xml中指定的,如果没有指定,则默认本地文件系统public static FileSystem get(Configuration conf) throws IOExceptionpublic static FileSystem newInstance(Configuration conf) throws IOException//通过给定URI方案和权限来确定要使用的文件系统,若URI中未指定方案,返回默认文件系统public static FileSystem get(URI uri, Configuration conf) throws IOExceptionpublic static FileSystem newInstance(URI uri, Configuration conf) throws IOException//作为给定用户来访问文件系统,对安全来说很重要public static FileSystem get(final URI uri, final Configuration conf, final String user) throws IOException, InterruptedExceptionpublic static FileSystem newInstance(final URI uri, final Configuration conf, final String user) throws IOException, InterruptedException
另外可以通过getLocal()或newInstanceLocal()获取本地文件系统:
public static LocalFileSystem getLocal(Configuration conf) throws IOExceptionpublic static LocalFileSystem newInstanceLocal(Configuration conf) throws IOException
二. 读取数据
1. 从hadoop url读取数据
读取文件最简单的方法是使用java.net.URL对象打开数据流,从中读取数据,但让java程序能识别hadoop的hdfs url需要通过FsUrlStreamHandlerFactory实例调用java.net.URL对象的setURLStreamHandlerFactory方法。
java测试类代码:
public class ReadFromHadoopURL { static { URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory()); } public static void main(String[] args) throws Exception{ String uri = "hdfs://localhost:9000/input/input1.txt"; InputStream in = null; try{ in = new URL(uri).openStream(); IOUtils.copyBytes(in, System.out, 4096, false); }finally{ IOUtils.closeStream(in); } }}
注:
这种文件读取的方法具有一定的限制性。因为Java.net.URL的setURLStreamHandlerFactory方法每个java虚拟机最多调用一次,如果程序中有不受自己控制的第三方组件调用了这个方法,将无法使用这种方法从hadoop中读取数据。
附setURLStreamHandlerFactory源码:
public static void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) { synchronized (streamHandlerLock) { if (factory != null) { throw new Error("factory already defined"); } SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkSetFactory(); } handlers.clear(); factory = fac; }}
2.通过FileSystem API读取数据
hadoop文件系统中通过org.apache.hadoop.fs.Path对象来代表文件。
获取到FileSystem实例后通过open()方法获取文件的输入流
//缓冲区默认大小4KB,bufferSize指定缓冲区大小public FSDataInputStream open(Path f) throws IOExceptionpublic abstract FSDataInputStream open(Path f, int bufferSize) throws IOException;
例:
java测试类代码:
public class ReadFromFileSystemAPI { public static void main(String[] args) throws Exception{ String uri = "hdfs://localhost:9000/input/input1.txt"; Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(URI.create(uri), conf); //第二种获取文件系统的方法 //FileSystem fs = FileSystem.newInstance(URI.create(uri), conf); InputStream in = null; try{ in = fs.open(new Path(uri)); IOUtils.copyBytes(in, System.out, 4096, false); }finally{ IOUtils.closeStream(in); } }}
输入流FSDataInputStream对象介绍
FileSystem对象中的open()方法返回的是org.apache.hadoop.fs.FSDataInputStream对象,这个对象继承了java.io.DataInputStream,并支持随机访问,从流的任意位置读取数据。
public class FSDataInputStream extends DataInputStream implements Seekable, PositionedReadable, ByteBufferReadable, HasFileDescriptor, CanSetDropBehind, CanSetReadahead, HasEnhancedByteBufferAccess{//implementation}
Seekable接口支持在文件中找到指定位置,并提供一个查询当前位置相对于文件起始位置偏移量的方法。注:seek()方法开销相对高,需要慎用。
public interface Seekable { //定位到从文件起始位置开始指定的偏移量的位置,若偏移量超出文件位置会报异常 void seek(long pos) throws IOException; //返回当前位置相对于文件起始位置的偏移量 long getPos() throws IOException; //查找数据的其他副本,若找到一个新副本则返回true,否则返回false boolean seekToNewSource(long targetPos) throws IOException;}PositionedReadable接口从一个指定偏移量处读取文件的一部分。public interface PositionedReadable { //从文件指定position处读取至多length字节的数据,并存入缓冲区buffer的指定偏移量offset处 //返回值是督导的字节数,可能比length的长度小 public int read(long position, byte[] buffer, int offset, int length) throws IOException; //从文件指定position处读取指定length的字节,并存入缓冲区buffer指定偏移量offset处 //若读到文件末尾仍不足length字节,则抛出异常 public void readFully(long position, byte[] buffer, int offset, int length) throws IOException; //从文件指定position处读取缓冲区buffer大小的字节,并存入buffer //若读到文件末尾仍不足length字节,则抛出异常 public void readFully(long position, byte[] buffer) throws IOException;}
例:
测试代码:
public class TestFSDataInputStream { private FileSystem fs = null; private FSDataInputStream in = null; private String uri = "hdfs://localhost:9000/input/input1.txt"; private Logger log = Logger.getLogger(TestFSDataInputStream.class); static{ PropertyConfigurator.configure("conf/log4j.properties"); } @Before public void setUp() throws Exception { Configuration conf = new Configuration(); fs = FileSystem.get(URI.create(uri), conf); } @Test public void test() throws Exception{ try{ in = fs.open(new Path(uri)); log.info("文件内容:"); IOUtils.copyBytes(in, System.out, 4096, false); in.seek(6); Long pos = in.getPos(); log.info("当前偏移量:"+pos); log.info("读取内容:"); IOUtils.copyBytes(in, System.out, 4096, false); byte[] bytes = new byte[10]; int num = in.read(7, bytes, 0, 10); log.info("从偏移量7读取10个字节到bytes,共读取"+num+"字节"); log.info("读取内容:"+(new String(bytes))); //以下代码会抛出EOFException// in.readFully(6, bytes);// in.readFully(6, bytes, 0, 10); }finally{ IOUtils.closeStream(in); } } }
三. 写入数据
1.新建文件
给准备建的文件指定一个Path对象,然后通过FileSystem的create()方法返回一个用于写入数据的输出流。
Create()方法有多个重载版本,允许指定是否需要强制覆盖现有文件、文件备份数量、写入文件时缓冲区大小、文件块大小及文件权限。还可指定Progressable回调接口,这样可以把数据写入datanode的进度通知给应用。
Create()方法能为需要写入且当前不存在的文件创建父目录,若不希望这样,则应先调用exists()方法检查父目录是否存在。
create()方法的所有重载方法:
//创建一个输出流,默认覆盖现有文件 public FSDataOutputStream create(Path f) throws IOException //创建一个输出流,文件存在时,overwrite为true则覆盖现有文件,为false则抛出异常public FSDataOutputStream create(Path f, boolean overwrite) throws IOException //创建一个输出流,默认覆盖现有文件,progress用来报告进度public FSDataOutputStream create(Path f, Progressable progress) throws IOException //创建一个输出流,默认覆盖现有文件,replication指定文件备份数public FSDataOutputStream create(Path f, short replication) throws IOException //创建一个输出流,默认覆盖现有文件,replication指定文件备份数,progress用来报告进度public FSDataOutputStream create(Path f, short replication, Progressable progress) throws IOException //创建一个输出流,文件存在时,overwrite为true则覆盖现有文件,为false则抛出异常 //bufferSize指定写入时缓冲区大小public FSDataOutputStream create(Path f, boolean overwrite, int bufferSize) throws IOException //创建一个输出流,文件存在时,overwrite为true则覆盖现有文件,为false则抛出异常 // bufferSize指定写入时缓冲区大小,replication指定文件备份数,blockSize指定文件块大小public FSDataOutputStream create(Path f, boolean overwrite, int bufferSize, short replication, long blockSize ) throws IOException //创建一个输出流,文件存在时,overwrite为true则覆盖现有文件,为false则抛出异常 // bufferSize指定写入时缓冲区大小,replication指定文件备份数,blockSize指定文件块大小 // progress用来报告进度public FSDataOutputStream create(Path f, boolean overwrite, int bufferSize,short replication, long blockSize, Progressable progress ) throws IOException //创建一个输出流,文件存在时,overwrite为true则覆盖现有文件,为false则抛出异常 // bufferSize指定写入时缓冲区大小,replication指定文件备份数,blockSize指定文件块大小 // progress用来报告进度,permission指定文件权限public abstract FSDataOutputStream create(Path f, FsPermission permission, boolean overwrite, int bufferSize, short replication, long blockSize, Progressable progress) throws IOException; //创建一个输出流,permission指定文件权限, bufferSize指定写入时缓冲区大小 // replication指定文件备份数,progress用来报告进度 // flags指定创建标志,标志如下: // CREATE - 如果文件不存在则创建文件,否则抛出异常 // APPEND - 如果文件存在则向文件追加内容,否则抛出异常 // OVERWRITE - 文件存在时,覆盖现有文件,否则抛出异常 // CREATE|APPEND - 文件不存在时创建文件,文件已存在时向文件追加内容 // CREATE|OVERWRITE - 文件不存在时创建文件,否则覆盖已有文件 // SYNC_BLOCK - 强制关闭文件块,如果需要同步操作,每次写入后还需调用Syncable.hsync()方法public FSDataOutputStream create(Path f, FsPermission permission, EnumSet<CreateFlag> flags, int bufferSize, short replication, long blockSize, Progressable progress) throws IOException //创建一个输出流,permission指定文件权限, bufferSize指定写入时缓冲区大小 // replication指定文件备份数,progress用来报告进度,blockSize指定文件块大小 // checksumOpt指定校验和选项,若为空,则使用配置文件中的值 // flags指定创建标志,标志如下: // CREATE - 如果文件不存在则创建文件,否则抛出异常 // APPEND - 如果文件存在则向文件追加内容,否则抛出异常 // OVERWRITE - 文件存在时,覆盖现有文件,否则抛出异常 // CREATE|APPEND - 文件不存在时创建文件,文件已存在时向文件追加内容 // CREATE|OVERWRITE - 文件不存在时创建文件,否则覆盖已有文件 // SYNC_BLOCK - 强制关闭文件块,如果需要同步操作,每次写入后还需调用Syncable.hsync()方法public FSDataOutputStream create(Path f, FsPermission permission, EnumSet<CreateFlag> flags, int bufferSize, short replication, long blockSize, Progressable progress, ChecksumOpt checksumOpt) throws IOException
例:
写入前HDFS中目录结构:
测试代码:
public class WriteByCreate { static{ PropertyConfigurator.configure("conf/log4j.properties"); } @Test public void createTest() throws Exception { String localSrc = "/home/hadoop/merge.txt"; String dst = "hdfs://localhost:9000/input/merge.txt"; InputStream in = new BufferedInputStream(new FileInputStream(localSrc)); Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(URI.create(dst), conf); OutputStream out = null; try{ out = fs.create(new Path(dst), new Progressable() { public void progress() { System.out.print("."); } }); Log.info("write start!"); IOUtils.copyBytes(in, out, 4096, true); System.out.println(); Log.info("write end!"); }finally{ IOUtils.closeStream(in); IOUtils.closeStream(out); } }}
2.向已存在文件末尾追加数据
FileSystem的append()方法允许在一个已存在文件的最后偏移量处追加数据。追加操作是可选的,并不是所有hadoop文件系统都实现了该操作。
Append()的重载方法
//向指定文件中追加数据,默认缓冲区大小4096,文件不存在时抛出异常public FSDataOutputStream append(Path f) throws IOException//向指定文件中追加数据,bufferSize指定缓冲区大小,文件不存在时抛出异常public FSDataOutputStream append(Path f, int bufferSize) throws IOException//向指定文件中追加数据,bufferSize指定缓冲区大小,文件不存在时抛出异常,progress报告进度public abstract FSDataOutputStream append(Path f, int bufferSize, Progressable progress) throws IOException;
例:
测试代码
public class WriteByAppend{ static{ PropertyConfigurator.configure("conf/log4j.properties"); } @Test public void appendTest() throws Exception { String localSrc = "/home/hadoop/merge.txt"; String dst = "hdfs://localhost:9000/input/merge.txt"; InputStream in = new BufferedInputStream(new FileInputStream(localSrc)); Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(URI.create(dst), conf); OutputStream out = null; try{ out = fs.append(new Path(dst),4096, new Progressable() { public void progress() { System.out.print("."); } }); Log.info("write start!"); IOUtils.copyBytes(in, out, 4096, true); System.out.println(); Log.info("write end!"); }finally{ IOUtils.closeStream(in); IOUtils.closeStream(out); } }}
输出流FSDataOutputStream对象
FileSystem的create()方法及append()方法返回的是FSDataOutputStream对象,它也有一个查询文件当前位置的方法getPos()。与FSDataInputStream不同,FSDataOutputStream不允许在文件中定位,因为HDFS只允许对一个已打开的文件顺序写入,或在现有文件末尾追加数据,不支持在除文件末尾外的其他位置进行写入,因此写入时定位没有意义。
• 四. 创建目录
FileSystem提供了创建目录的方法。可以一次性创建所有必要但还没有的父目录。
public boolean mkdirs(Path f) throws IOException public abstract boolean mkdirs(Path f, FsPermission permission ) throws IOException;
通常不需要显示创建一个目录,因为调用create()方法写入文件时会自动创建父目录。
五. 查询文件系统
文件元数据FileStatus
FileStatus类封装了文件系统中文件和目录的元数据,FileStatus源码中可以看到如下属性
public class FileStatus implements Writable, Comparable { private Path path;//文件或目录的path private long length;//文件字节数 private boolean isdir;//是否是目录 private short block_replication;//文件块备份数 private long blocksize;//文件块大小 private long modification_time;//修改时间 private long access_time;//访问时间 private FsPermission permission;//权限 private String owner;//所属用户 private String group;//所属用户组 private Path symlink; //软连接 //method}
FileSystem的getFileStatus()方法用于获取文件或目录的FileStatus对象
例:
public class ShowFileStatus { private MiniDFSCluster cluster; // use an in-process HDFS cluster for testing private FileSystem fs; @Before public void setUp() throws IOException { Configuration conf = new Configuration(); if (System.getProperty("test.build.data") == null) { System.setProperty("test.build.data", "/tmp"); } cluster = new MiniDFSCluster(conf, 1, true, null); fs = cluster.getFileSystem(); OutputStream out = fs.create(new Path("/dir/file")); out.write("content".getBytes("UTF-8")); out.close(); } @After public void tearDown() throws IOException { if (fs != null) { fs.close(); } if (cluster != null) { cluster.shutdown(); } } @Test(expected = FileNotFoundException.class) public void throwsFileNotFoundForNonExistentFile() throws IOException { fs.getFileStatus(new Path("no-such-file")); } @Test public void fileStatusForFile() throws IOException { Path file = new Path("/dir/file"); Log.info("文件filestatus:"); FileStatus stat = fs.getFileStatus(file); Log.info("path:"+stat.getPath().toUri().getPath()); Log.info("isdir:"+String.valueOf(stat.isDir())); Log.info("length:"+String.valueOf(stat.getLen())); Log.info("modification:"+String.valueOf(stat.getModificationTime())); Log.info("replication:"+String.valueOf(stat.getReplication())); Log.info("blicksize:"+String.valueOf(stat.getBlockSize())); Log.info("owner:"+stat.getOwner()); Log.info("group:"+stat.getGroup()); Log.info("permission:"+stat.getPermission().toString()); } @Test public void fileStatusForDirectory() throws IOException { Path dir = new Path("/dir"); Log.info("目录filestatus:"); FileStatus stat = fs.getFileStatus(dir); Log.info("path:"+stat.getPath().toUri().getPath()); Log.info("isdir:"+String.valueOf(stat.isDir())); Log.info("length:"+String.valueOf(stat.getLen())); Log.info("modification:"+String.valueOf(stat.getModificationTime())); Log.info("replication:"+String.valueOf(stat.getReplication())); Log.info("blicksize:"+String.valueOf(stat.getBlockSize())); Log.info("owner:"+stat.getOwner()); Log.info("group:"+stat.getGroup()); Log.info("permission:"+stat.getPermission().toString()); }}
列出文件
列出目录中内容,可以使用FileSystem的listStatus()方法。方法接收一个或一组路径,如果路径是文件,以数组方法返回长度为1的FileStatus对象,如果路径是目录,返回0个或多个FileStatus对象表示目录中包含的文件或目录;如果是一组路径,依次轮流对每个路径调用listStatus方法,将结果累积到一个数组
方法如下
//列出给定路径下的文件或目录的status public abstract FileStatus[] listStatus(Path f) throws FileNotFoundException, IOException;//列出给定路径下符合用户提供的filter限制的文件或目录的statuspublic FileStatus[] listStatus(Path f, PathFilter filter) throws FileNotFoundException, IOException //列出给定的一组路径下文件或目录的statuspublic FileStatus[] listStatus(Path[] files) throws FileNotFoundException, IOException //列出给定的一组路径下符合用户提供的filter限制的文件或目录的statuspublic FileStatus[] listStatus(Path[] files, PathFilter filter) throws FileNotFoundException, IOException
例:
public class ListFileStatus { private FileSystem fs = null; private String uri = "hdfs://localhost:9000/input/input1.txt"; private Path[] paths = new Path[]{new Path("/input.zip"),new Path("/input/"),new Path("/output/")}; private Logger log = Logger.getLogger(TestFSDataInputStream.class); static{ PropertyConfigurator.configure("conf/log4j.properties"); } @Before public void setUp() throws Exception { Configuration conf = new Configuration(); fs = FileSystem.get(URI.create(uri), conf); } @Test public void listStatusTest() throws Exception { log.info("--------------------------------"); log.info("列出文件 ["+paths[0]+"] 的status:"); FileStatus[] status = fs.listStatus(paths[0]); printFileStatus(status); log.info("--------------------------------"); log.info("--------------------------------"); log.info("列出目录 ["+paths[1]+"] 的status:"); status = fs.listStatus(paths[1]); printFileStatus(status); log.info("--------------------------------"); log.info("--------------------------------"); log.info("列出一组path "+Arrays.toString(paths)+" 的status:"); status = fs.listStatus(paths); printFileStatus(status); log.info("--------------------------------"); } protected void printFileStatus(FileStatus[] status){ for (FileStatus s : status) { log.info(s.getPath()+" status:"); log.info("isdir:"+String.valueOf(s.isDir())); log.info("length:"+String.valueOf(s.getLen())); log.info("modification:"+String.valueOf(s.getModificationTime())); log.info("replication:"+String.valueOf(s.getReplication())); log.info("blicksize:"+String.valueOf(s.getBlockSize())); log.info("owner:"+s.getOwner()); log.info("group:"+s.getGroup()); log.info("permission:"+s.getPermission().toString()); log.info("\n"); } }}
另外,需要在一次操作中处理一批文件时,hadoop提供了通配符来匹配多个文件。
通配符 名称 匹配
* 星号 匹配0或多个字符
? 问号 匹配单衣字符
[ab] 字符类 匹配{a,b}集合里的一个字符
[^ab] 非字符类 匹配非{a,b}集合里的一个字符
[a-b] 字符范围 匹配一个{a,b}范围内的字符,包括ab,a的字典顺序要小于等于b
[^a-b] 非字符范围 匹配一个不在{a,b}范围内的字符,包括ab,a的字典顺序要小于等于b
{a,b} 或选择 匹配包含a或b中一个的
\c 转义字符 匹配原字符c
hadoop的FileSystem为通配提供了2个globStatus()方法,方法返回所有文件路径与给定的通配符相匹配的文件的FileStatus,filter可进一步对匹配进行限制:
public FileStatus[] globStatus(Path pathPattern) throws IOExceptionpublic FileStatus[] globStatus(Path pathPattern, PathFilter filter) throws IOException
例:
目录结构
/
├── 2007/
│ └── 12/
│ ├── 30/
│ └── 31/
└── 2008/
└── 01/
├── 01/
└── 02/
通配符示例:
通配符 Expansion
/* /2007 /2008
// /2007/12 /2008/01
//12/ /2007/12/30 /2007/12/31
/200? /2007 /2008
/200[78] /2007 /2008
/200[7-8] /2007 /2008
/200[^01234569] /2007 /2008
///{31,01} /2007/12/31 /2008/01/01
///3{0,1} /2007/12/30 /2007/12/31
/*/{12/31,01/01} /2007/12/31 /2008/01/01
测试代码:
public class ListFileStatus { private FileSystem fs = null; private String uri = "hdfs://localhost:9000/input/input1.txt"; private Path[] globPaths = new Path[]{new Path("/*"),new Path("/*/*"),new Path("/*/12/*"),new Path("/200?") ,new Path("/200[78]"),new Path("/200[7-8]"),new Path("/200[^01234569]") ,new Path("/*/*/{31,01}"),new Path("/*/*/3{0,1}"),new Path("/*/{12/31,01/01}")}; private Logger log = Logger.getLogger(TestFSDataInputStream.class); static{ PropertyConfigurator.configure("conf/log4j.properties"); } @Before public void setUp() throws Exception { Configuration conf = new Configuration(); fs = FileSystem.get(URI.create(uri), conf); } @Test public void globStatusTest() throws Exception { for(Path p:globPaths){ log.info("glob ["+p+"]: "); FileStatus[] status = fs.globStatus(p); printFilePath(status); } } protected void printFilePath(FileStatus[] status){ Path[] listedPaths = FileUtil.stat2Paths(status); for (Path p : listedPaths) { log.info(p); } log.info(""); }}
通配符并不总能精确的描述的描述想要访问的文件集,如使用通配符排除一个特定的文件就不太可能。FileSystem的listStatus()方法和globStatus()方法提供可选的PathFilter对象,以编程方式控制通配符。过滤器只能作用于文件名,不能针对文件属性进行过滤
PathFilter接口:
public interface PathFilter { boolean accept(Path path);}
例:
测试代码
public class ListFileStatus { private FileSystem fs = null; private String uri = "hdfs://localhost:9000/input/input1.txt"; private Logger log = Logger.getLogger(TestFSDataInputStream.class); static{ PropertyConfigurator.configure("conf/log4j.properties"); } @Before public void setUp() throws Exception { Configuration conf = new Configuration(); fs = FileSystem.get(URI.create(uri), conf); } @Test public void pathFilterTest() throws Exception { log.info("glob [/2007/*/*]: "); FileStatus[] status = fs.globStatus(new Path("/2007/*/*")); printFilePath(status); log.info("glob [/2007/*/*] except [/2007/12/31]: "); status = fs.globStatus(new Path("/2007/*/*"), new RegexExcludePathFilter("^.*/2007/12/31$")); printFilePath(status); } protected void printFilePath(FileStatus[] status){ Path[] listedPaths = FileUtil.stat2Paths(status); for (Path p : listedPaths) { log.info(p); } log.info(""); } class RegexExcludePathFilter implements PathFilter { private final String regex; public RegexExcludePathFilter(String regex) { this.regex = regex; } public boolean accept(Path path) { return !path.toString().matches(regex); } }}
六. 删除数据
FileSystem的delete()方法可以永久删除文件或目录。
public boolean delete(Path f) throws IOException //recursive为true时,非空目录及其内容才会被删除,否则抛出异常public abstract boolean delete(Path f, boolean recursive) throws IOException//标记当文件系统关闭时将删除的文件。当JVM关闭时,被标记的文件将被删除public boolean deleteOnExit(Path f) throws IOException
- 用Java代码实现hadoop命令
- VM虚拟机上安装Eclipse和Hadoop上文件命令操作的java代码实现
- java实现hadoop的cat命令
- JAVA代码实现执行CMD命令
- JAVA代码实现执行CMD命令
- hadoop入门之通过java代码实现将本地文件上传到hadoop的文件系统
- Hadoop Shell命令实现原理
- 为什么Hadoop是用Java实现的?
- Hadoop:Java API实现
- linux下用代码实现系统命令
- linux系统wget命令的java代码实现
- JAVA代码实现执行CMD命令的方法
- 通过java代码实现dos环境下多条命令的执行
- java代码实现ping命令(支持linux系统)
- 用java实现native2asscii命令
- 用java代码实现sqoop代码
- Hadoop入门(10)_通过java代码实现从本地的文件上传到Hadoop的文件系统
- Hadoop入门(10)_通过java代码实现从本地的文件上传到Hadoop的文件系统
- 线性动态规划 (共六题)
- 对“回文数”问题的解法
- 同源策略和跨域访问
- 找米网2017域名投资经验分享(精华总结)
- Java 内存区域介绍
- 用Java代码实现hadoop命令
- leetcode--22. Generate Parentheses
- 计算机网络-输入一个网址后发生的事情
- 数组——基本概念
- 主席树入门学习
- jquery event type
- python3 数据类型归纳
- Android Widget 开发详解(二)——支持listView滑动的widget
- varnish服务配置