一 HDFS客戶端環境准備
1.1 jar包准備
1)解壓hadoop-2.7.6.tar.gz到非中文目錄
2)進入share文件夾,查找所有jar包,並把jar包拷貝到_lib文件夾下
3)在全部jar包中查找sources.jar,並剪切到_source文件夾。
4)在全部jar包中查找tests.jar,並剪切到_test文件夾
1.2 Eclipse准備
1)根據自己電腦的操作系統拷貝對應的編譯后的hadoop jar包到非中文路徑(例如:E:\02_software\hadoop-2.7.6)。(如果不生效,重新啟動eclipse)
2)配置HADOOP_HOME環境變量
3)創建第一個java工程HdfsClientDemo1
4)創建lib文件夾,然后添加jar包
5)創建包,HdfsClient測試類
public class HdfsClient { // 上傳文件 public static void main(String[] args) throws IOException, InterruptedException, URISyntaxException { // 1 獲取文件系統 Configuration configuration = new Configuration(); // 配置在集群上運行 // configuration.set("fs.defaultFS", "hdfs://node21:9000"); // FileSystem fs = FileSystem.get(configuration); FileSystem fs = FileSystem.get(new URI("hdfs://node21:9000"), configuration, "admin"); // 2 上傳文件 fs.copyFromLocalFile(new Path("e:/hello.txt"), new Path("/hello2.txt")); // 3 關閉資源 fs.close(); System.out.println("over"); } }
6)執行程序
運行時需要配置用戶名稱,客戶端去操作hdfs時,是有一個用戶身份的。默認情況下,hdfs客戶端api會從jvm中獲取一個參數來作為自己的用戶身份:-DHADOOP_USER_NAME=admin,admin為用戶名稱。
7)注意:如果eclipse打印不出日志,在控制台上只顯示
1.log4j:WARN No appenders could be found for logger (org.apache.hadoop.util.Shell). 2.log4j:WARN Please initialize the log4j system properly. 3.log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
需要在項目的src目錄下,新建一個文件,命名為“log4j.properties”,在文件中填入
log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n log4j.appender.logfile=org.apache.log4j.FileAppender log4j.appender.logfile.File=target/spring.log log4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
二 HDFS的高級API編程
2.1 HDFS文件上傳(測試參數優先級)
1.代碼
@Test public void testCopyFromLocalFile() throws IOException, InterruptedException, URISyntaxException { // 1 獲取文件系統 Configuration configuration = new Configuration(); configuration.set("dfs.replication", "2"); FileSystem fs = FileSystem.get(new URI("hdfs://node21:9000"), configuration, "admin"); // 2 上傳文件 fs.copyFromLocalFile(new Path("e:/hello.txt"), new Path("/hello5.txt")); // 3 關閉資源 fs.close(); System.out.println("over"); }
2.將hdfs-site.xml拷貝到項目的根目錄下
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <configuration> <property> <name>dfs.replication</name> <value>1</value> </property> </configuration>
3.測試參數優先級
參數優先級: (1)客戶端代碼中設置的值 >(2)classpath下的用戶自定義配置文件 >(3)然后是服務器的默認配置
2.2 文件的上傳和下載
package com.xyg.hdfs.api; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; public class HDFS_GET_AND_PUT { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); conf.set("fs.defaultFS", "hdfs://node21:9000"); conf.set("dfs.replication", "2"); FileSystem fs = FileSystem.get(conf); /** * 更改操作用戶有兩種方式: * 1、直接設置運行換種的用戶名為hadoop * VM arguments ; -DHADOOP_USER_NAME=admin * 2、在代碼中進行聲明 * System.setProperty("HADOOP_USER_NAME", "admin"); */ System.setProperty("HADOOP_USER_NAME", "admin"); // 上傳 fs.copyFromLocalFile(new Path("c:/sss.txt"), new Path("/a/ggg.txt")); /** * .crc : 校驗文件 * 每個塊的元數據信息都只會記錄合法數據的起始偏移量: qqq.txt blk_41838 : 0 - 1100byte * 如果進行非法的數據追加。最終是能夠下載合法數據。 * 由於你在數據的中間, 也就是說在 0 -1100 之間的范圍進行了數據信息的更改。 造成了采用CRC算法計算出來校驗值,和最初存入進HDFS的校驗值 * 不一致。HDFS就認為當前這個文件被損壞了。 */ // 下載 fs.copyToLocalFile(new Path("/a/qqq.txt"), new Path("c:/qqq3.txt")); /** * 上傳和下載的API的底層封裝其實就是 : FileUtil.copy(....) */ fs.close(); } }
2、配置文件conf
package com.xyg.hdfs; import java.io.IOException; import java.util.Iterator; import java.util.Map.Entry; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; public class TestConf1 { public static void main(String[] args) throws Exception { /** * 底層會加載一堆的配置文件: * * core-default.xml * hdfs-default.xml * mapred-default.xml * yarn-default.xml */ Configuration conf = new Configuration(); // conf.addResource("hdfs-default.xml"); /** * 當前這個hdfs-site.xml文件就放置在這個項目中的src下。也就是classpath路徑下。 * 所以 FS在初始化的時候,會把hdfs-site.xml這個文件中的name-value對解析到conf中 * 但是: * 1、如果hdfs-site.xml 不在src下, 看是否能加載??? 不能 * 2、如果文件名不叫做 hdfs-default.xml 或者 hdsf-site.xml 看是否能自動加載??? 不能 * 得出的結論: * 如果需要項目代碼自動加載配置文件中的信息,那么就必須把配置文件改成-default.xml或者-site.xml的名稱 * 而且必須放置在src下 * * 那如果不叫這個名,或者不在src下,也需要加載這些配置文件中的參數: * 必須使用conf對象提供的一些方法去手動加載 */ // conf.addResource("hdfs-site.xml"); conf.set("dfs.replication", "1"); conf.addResource("myconfig/hdfs-site.xml"); /** * 依次加載的參數信息的順序是: * 1、加載 core/hdfs/mapred/yarn-default.xml * 2、加載通過conf.addResources()加載的配置文件 * 3、加載conf.set(name, value) */ FileSystem fs = FileSystem.get(conf); System.out.println(conf.get("dfs.replication")); Iterator<Entry<String, String>> iterator = conf.iterator(); while(iterator.hasNext()){ Entry<String, String> e = iterator.next(); System.out.println(e.getKey() + "\t" + e.getValue()); } } }
輸出結果

3、列出指定目錄下的文件以及塊的信息
package com.xyg.hdfs; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.BlockLocation; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.LocatedFileStatus; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.RemoteIterator; public class TestHDFS1 { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); System.setProperty("HADOOP_USER_NAME", "admin"); conf.set("fs.defaultFS", "hdfs://node21:9000"); FileSystem fs = FileSystem.get(conf); /** * 列出指定的目錄下的所有文件 */ RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true); while(listFiles.hasNext()){ LocatedFileStatus file = listFiles.next(); System.out.println(file.getPath()+"\t"); System.out.println(file.getPath().getName()+"\t"); System.out.println(file.getLen()+"\t"); System.out.println(file.getReplication()+"\t"); /** * blockLocations的長度是幾? 是什么意義? * * 塊的數量 */ BlockLocation[] blockLocations = file.getBlockLocations(); System.out.println(blockLocations.length+"\t"); for(BlockLocation bl : blockLocations){ String[] hosts = bl.getHosts(); System.out.print(hosts[0] + "-" + hosts[1]+"\t"); } System.out.println(); } } }
輸出結果
hdfs://hadoop1:9000/aa/bb/cc/hadoop.tar.gz hadoop.tar.gz 199007110 2 3 hadoop3-hadoop1 hadoop1-hadoop2 hadoop1-hadoop4
4、上傳文件
package com.xyg.hdfs; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils; public class UploadDataByStream { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); System.setProperty("HADOOP_USER_NAME", "admin"); conf.set("fs.defaultFS", "hdfs://node21:9000"); FileSystem fs = FileSystem.get(conf); InputStream in = new FileInputStream(new File("d:/abc.tar.gz")); FSDataOutputStream out = fs.create(new Path("/aa/abc.tar.gz")); IOUtils.copyBytes(in, out, 4096, true); fs.close(); } }
5、下載文件
package com.xyg.hdfs; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils; public class DownloadDataByStream { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); System.setProperty("HADOOP_USER_NAME", "admin"); conf.set("fs.defaultFS", "hdfs://node21:9000"); FileSystem fs = FileSystem.get(conf); FSDataInputStream in = fs.open(new Path("/aa/abc.tar.gz")); OutputStream out = new FileOutputStream(new File("D:/abc.sh")); IOUtils.copyBytes(in, out, 4096, true); fs.close(); } }
6、刪除某個路徑下特定類型的文件,比如class類型文件,比如txt類型文件
package com.xyg.hdfs; import java.net.URI; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; public class HDFS_DELETE_CLASS { public static final String FILETYPE = "tar.gz"; public static final String DELETE_PATH = "/aa"; public static void main(String[] args) throws Exception { new HDFS_DELETE_CLASS().rmrClassFile(new Path(DELETE_PATH)); } public void rmrClassFile(Path path) throws Exception{ // 首先獲取集群必要的信息,以得到FileSystem的示例對象fs Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(new URI("hdfs://node2:9000"), conf, "admin"); // 首先檢查path本身是文件夾還是目錄 FileStatus fileStatus = fs.getFileStatus(path); boolean directory = fileStatus.isDirectory(); // 根據該目錄是否是文件或者文件夾進行相應的操作 if(directory){ // 如果是目錄 checkAndDeleteDirectory(path, fs); }else{ // 如果是文件,檢查該文件名是不是FILETYPE類型的文件 checkAndDeleteFile(path, fs); } } // 處理目錄 public static void checkAndDeleteDirectory(Path path, FileSystem fs) throws Exception{ // 查看該path目錄下一級子目錄和子文件的狀態 FileStatus[] listStatus = fs.listStatus(path); for(FileStatus fStatus: listStatus){ Path p = fStatus.getPath(); // 如果是文件,並且是以FILETYPE結尾,則刪掉,否則繼續遍歷下一級目錄 if(fStatus.isFile()){ checkAndDeleteFile(p, fs); }else{ checkAndDeleteDirectory(p, fs); } } } // 檢查文件是否符合刪除要求,如果符合要求則刪除,不符合要求則不做處理 public static void checkAndDeleteFile(Path path, FileSystem fs) throws Exception{ String name = path.getName(); System.out.println(name); /*// 直接判斷有沒有FILETYPE這個字符串,不是特別穩妥,並且會有誤操作,所以得判斷是不是以FILETYPE結尾 if(name.indexOf(FILETYPE) != -1){ fs.delete(path, true); }*/ // 判斷是不是以FILETYPE結尾 int startIndex = name.length() - FILETYPE.length(); int endIndex = name.length(); // 求得文件后綴名 String fileSuffix = name.substring(startIndex, endIndex); if(fileSuffix.equals(FILETYPE)){ fs.delete(path, true); } } }
7、刪除HDFS集群中的所有空文件和空目錄
public class DeleteEmptyDirAndFile { static FileSystem fs = null; public static void main(String[] args) throws Exception { initFileSystem(); // 創建測試數據 // makeTestData(); // 刪除測試數據 // deleteTestData(); // 刪除指定文件夾下的空文件和空文件夾 deleteEmptyDirAndFile(new Path("/aa")); } /** * 刪除指定文件夾下的 空文件 和 空文件夾 * @throws Exception */ public static void deleteEmptyDirAndFile(Path path) throws Exception { //當是空文件夾時 FileStatus[] listStatus = fs.listStatus(path); if(listStatus.length == 0){ fs.delete(path, true); return; } // 該方法的結果:包括指定目錄的 文件 和 文件夾 RemoteIterator<LocatedFileStatus> listLocatedStatus = fs.listLocatedStatus(path); while (listLocatedStatus.hasNext()) { LocatedFileStatus next = listLocatedStatus.next(); Path currentPath = next.getPath(); // 獲取父目錄 Path parent = next.getPath().getParent(); // 如果是文件夾,繼續往下遍歷,刪除符合條件的文件(空文件夾) if (next.isDirectory()) { // 如果是空文件夾 if(fs.listStatus(currentPath).length == 0){ // 刪除掉 fs.delete(currentPath, true); }else{ // 不是空文件夾,那么則繼續遍歷 if(fs.exists(currentPath)){ deleteEmptyDirAndFile(currentPath); } } // 如果是文件 } else { // 獲取文件的長度 long fileLength = next.getLen(); // 當文件是空文件時, 刪除 if(fileLength == 0){ fs.delete(currentPath, true); } } // 當空文件夾或者空文件刪除時,有可能導致父文件夾為空文件夾, // 所以每次刪除一個空文件或者空文件的時候都需要判斷一下,如果真是如此,那么就需要把該文件夾也刪除掉 int length = fs.listStatus(parent).length; if(length == 0){ fs.delete(parent, true); } } } /** * 初始化FileSystem對象之用 */ public static void initFileSystem() throws Exception{ Configuration conf = new Configuration(); System.setProperty("HADOOP_USER_NAME", "admin"); conf.addResource("config/core-site.xml"); conf.addResource("config/hdfs-site.xml"); fs = FileSystem.get(conf); } /** * 創建 測試 數據之用 */ public static void makeTestData() throws Exception { String emptyFilePath = "D:\\bigdata\\1704mr_test\\empty.txt"; String notEmptyFilePath = "D:\\bigdata\\1704mr_test\\notEmpty.txt"; // 空文件夾 和 空文件 的目錄 String path1 = "/aa/bb1/cc1/dd1/"; fs.mkdirs(new Path(path1)); fs.mkdirs(new Path("/aa/bb1/cc1/dd2/")); fs.copyFromLocalFile(new Path(emptyFilePath), new Path(path1)); fs.copyFromLocalFile(new Path(notEmptyFilePath), new Path(path1)); // 空文件 的目錄 String path2 = "/aa/bb1/cc2/dd2/"; fs.mkdirs(new Path(path2)); fs.copyFromLocalFile(new Path(emptyFilePath), new Path(path2)); // 非空文件 的目錄 String path3 = "/aa/bb2/cc3/dd3"; fs.mkdirs(new Path(path3)); fs.copyFromLocalFile(new Path(notEmptyFilePath), new Path(path3)); // 空 文件夾 String path4 = "/aa/bb2/cc4/dd4"; fs.mkdirs(new Path(path4)); System.out.println("測試數據創建成功"); } /** * 刪除 指定文件夾 * @throws Exception */ public static void deleteTestData() throws Exception { boolean delete = fs.delete(new Path("/aa"), true); System.out.println(delete ? "刪除數據成功" : "刪除數據失敗"); } }
8、手動拷貝某個特定的數據塊(比如某個文件的第二個數據塊)
/** * 手動拷貝某個特定的數據塊(比如某個文件的第二個數據塊) * */ public static void copyBlock(String str,int num) { Path path = new Path(str); BlockLocation[] localtions = new BlockLocation[0] ; try { FileStatus fileStatus = fs.getFileStatus(path); localtions = fs.getFileBlockLocations(fileStatus, 0, fileStatus.getLen()); /*for(int i=0;i<localtions.length;i++) { System.out.println(localtions[i]); }*/ /*System.out.println(localtions[num-1].getOffset()); System.out.println(localtions[num-1].getLength()); String[] hosts = localtions[num-1].getHosts();*/ FSDataInputStream open = fs.open(path); open.seek(localtions[num-1].getOffset()); OutputStream out = new FileOutputStream(new File("D:/abc.tar.gz")); IOUtils.copyBytes(open, out,4096,true); } catch (IOException e) { e.printStackTrace(); } }
9、編寫程序統計出HDFS文件系統中文件大小小於HDFS集群中的默認塊大小的文件占比
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.LocatedFileStatus; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.RemoteIterator; /** * 編寫程序統計出HDFS文件系統中文件大小小於HDFS集群中的默認塊大小的文件占比 * 比如:大於等於128M的文件個數為98,小於128M的文件總數為2,所以答案是2% */ public class Exam1_SmallFilePercent { private static int DEFAULT_BLOCKSIZE = 128 * 1024 * 1024; public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); conf.set("fs.defaultFS", "hdfs://node21:9000"); System.setProperty("HADOOP_USER_NAME", "admin"); FileSystem fs = FileSystem.get(conf); Path path = new Path("/"); float smallFilePercent = getSmallFilePercent(fs, path); System.out.println(smallFilePercent); fs.close(); } /** * 該方法求出指定目錄下的小文件和總文件數的對比 * @throws Exception */ private static float getSmallFilePercent(FileSystem fs, Path path) throws Exception { // TODO Auto-generated method stub int smallFile = 0; int totalFile = 0; RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(path, false); while(listFiles.hasNext()){ totalFile++; LocatedFileStatus next = listFiles.next(); long len = next.getLen(); if(len < DEFAULT_BLOCKSIZE){ smallFile++; } } System.out.println(smallFile+" : "+totalFile); return smallFile * 1f /totalFile; } }
10、編寫程序統計出HDFS文件系統中的平均數據塊數(數據塊總數/文件總數)
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.LocatedFileStatus; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.RemoteIterator; /** * 編寫程序統計出HDFS文件系統中的平均數據塊數(數據塊總數/文件總數) * 比如:一個文件有5個塊,一個文件有3個塊,那么平均數據塊數為4 * 如果還有一個文件,並且數據塊就1個,那么整個HDFS的平均數據塊數就是3 */ public class Exam2_HDSFAvgBlocks { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); conf.set("fs.defaultFS", "hdfs://node21:9000"); System.setProperty("HADOOP_USER_NAME", "admin"); FileSystem fs = FileSystem.get(conf); Path path = new Path("/"); float avgHDFSBlocks = getHDFSAvgBlocks(fs, path); System.out.println("HDFS的平均數據塊個數為:" + avgHDFSBlocks); fs.close(); } /** * 求出指定目錄下的所有文件的平均數據塊個數 */ private static float getHDFSAvgBlocks(FileSystem fs, Path path) throws Exception { // TODO Auto-generated method stub int totalFiles = 0; // 總文件數 int totalBlocks = 0; // 總數據塊數 RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(path, false); while(listFiles.hasNext()){ LocatedFileStatus next = listFiles.next(); int length = next.getBlockLocations().length; totalBlocks += length; if(next.getLen() != 0){ totalFiles++; } } System.out.println(totalBlocks+" : "+totalFiles); return totalBlocks * 1f / totalFiles; } }
11、編寫程序統計出HDFS文件系統中的平均副本數(副本總數/總數據塊數)
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.LocatedFileStatus; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.RemoteIterator; /** * 編寫程序統計出HDFS文件系統中的平均副本數(副本總數/總數據塊數) * 比如:總共兩個文件,一個文件5個數據塊,每個數據塊3個副本,第二個文件2個數據塊,每個文件2個副本,最終的平均副本數 = (3*3 + 2*2)/(3+2)= 2.8 */ public class Exam3_HDSFAvgBlockCopys { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); conf.set("fs.defaultFS", "hdfs://node21:9000"); System.setProperty("HADOOP_USER_NAME", "admin"); FileSystem fs = FileSystem.get(conf); Path path = new Path("/"); float avgHDFSBlockCopys = getHDFSAvgBlockCopys(fs, path); System.out.println("HDFS的平均數據塊個數為:" + avgHDFSBlockCopys); fs.close(); } /** * 求出指定目錄下的所有文件的平均數據塊個數 */ private static float getHDFSAvgBlockCopys(FileSystem fs, Path path) throws Exception { // TODO Auto-generated method stub int totalCopy = 0; // 總副本數 int totalBlocks = 0; // 總數據塊數 RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(path, false); while(listFiles.hasNext()){ LocatedFileStatus next = listFiles.next(); int length = next.getBlockLocations().length; short replication = next.getReplication(); totalBlocks += length; totalCopy += length * replication; } System.out.println(totalCopy+" : "+totalBlocks); return totalCopy * 1f / totalBlocks; } }
12、統計HDFS整個文件系統中的不足指定數據塊大小的數據塊的比例
import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.BlockLocation; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.LocatedFileStatus; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.RemoteIterator; /** * 統計HDFS整個文件系統中的不足指定數據塊大小的數據塊的比例 * 比如指定的數據塊大小是128M,總數據塊有100個,不是大小為完整的128M的數據塊有5個,那么不足指定數據塊大小的數據塊的比例就為5% * 注意:千萬注意考慮不同文件的指定數據塊大小可能不一致。所以千萬不能用默認的128M一概而論 */ public class Exam4_LTBlockSize { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); conf.set("fs.defaultFS", "hdfs://node21:9000"); System.setProperty("HADOOP_USER_NAME", "admin"); FileSystem fs = FileSystem.get(conf); Path path = new Path("/"); float avgHDFSBlockCopys = getLessThanBlocksizeBlocks(fs, path); System.out.println("HDFS的不足指定數據塊大小的數據塊數目為:" + avgHDFSBlockCopys); fs.close(); } private static float getLessThanBlocksizeBlocks(FileSystem fs, Path path) throws Exception { // TODO Auto-generated method stub int totalBlocks = 0; // 總副本數 int lessThenBlocksizeBlocks = 0; // 總數據塊數 RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(path, false); while(listFiles.hasNext()){ LocatedFileStatus next = listFiles.next(); BlockLocation[] blockLocations = next.getBlockLocations(); int length = blockLocations.length; if(length != 0){ totalBlocks += length; long lastBlockSize = blockLocations[length - 1].getLength(); long blockSize = next.getBlockSize(); if(lastBlockSize < blockSize){ lessThenBlocksizeBlocks++; } } } System.out.println(lessThenBlocksizeBlocks+" : "+totalBlocks); return lessThenBlocksizeBlocks * 1f / totalBlocks; } }
13、統計出一個給定數組的蓄水總量(把數組的每個位置的數看是做地勢高低)
/** 統計出一個給定數組的蓄水總量(把數組的每個位置的數看是做地勢高低) 比如:int[] intArray = new int[]{4,3,2,5,6,4,4,7} 能蓄水:[0,1,2,0,0,2,2,0] 所以總量是:7 核心思路:把數組切成很多個 01數組,每一層一個01數組,統計每個01數組中的合法0的總個數(數組的左邊第一個1的中間區間中的0的個數)即可 */ public class Exam5_WaterStoreOfArray { public static void main(String[] args) { // int[] intArray = new int[]{4,3,2,5,6,4,4,7}; // int[] intArray = new int[]{1,2,3,4,5,6}; int[] intArray = new int[]{3,1,2,7,3,8,4,9,5,6}; int totalWater = getArrayWater(intArray); System.out.println(totalWater); } /** * 求出數組中的水數 */ private static int getArrayWater(int[] intArray) { int findMaxValueOfArray = findMaxValueOfArray(intArray); int findMinValueOfArray = findMinValueOfArray(intArray); int length = intArray.length; int totalWater = 0; // 循環次數就是最大值和最小值的差 for(int i=findMinValueOfArray; i<findMaxValueOfArray; i++){ // 循環構造每一層的01數組 int[] tempArray = new int[length]; for(int j=0; j<length; j++){ if(intArray[j] > i){ tempArray[j] = 1; }else{ tempArray[j] = 0; } } // 獲取每一個01數組的合法0個數 int waterOfOneZeroArray = getWaterOfOneZeroArray(tempArray); totalWater += waterOfOneZeroArray; } return totalWater; } /** * 尋找邏輯是:從左右開始各找一個1,然后這兩個1之間的所有0的個數,就是水數 */ private static int getWaterOfOneZeroArray(int[] tempArray) { int length = tempArray.length; int toatalWater = 0; // 找左邊的1 int i = 0; while(i < length){ if(tempArray[i] == 1){ break; } i++; } // 從右邊開始找1 int j=length-1; while(j >= i){ if(tempArray[j] == 1){ break; } j--; } // 找以上兩個1之間的0的個數。 if(i == j || i + 1 == j){ return 0; }else{ for(int k=i+1; k<j; k++){ if(tempArray[k] == 0){ toatalWater++; } } return toatalWater; } } /** * * 描述:找出一個數組中的最大值 */ public static int findMaxValueOfArray(int[] intArray){ int length = intArray.length; if(length == 0){ return 0; }else if(length == 1){ return intArray[0]; }else{ int max = intArray[0]; for(int i=1; i<length; i++){ if(intArray[i] > max){ max = intArray[i]; } } return max; } } /** * 找出一個數組中的最小值 */ public static int findMinValueOfArray(int[] intArray){ int length = intArray.length; if(length == 0){ return 0; }else if(length == 1){ return intArray[0]; }else{ int min = intArray[0]; for(int i=1; i<length; i++){ if(intArray[i] < min){ min = intArray[i]; } } return min; } } }