Java结构化数据类型基础

目录

内存中的数据结构

数组

集合

Set

List

Map

Queue

输入输出-磁盘文件中的数据结构

标准输入

标准输出

格式化输出Formatter和printf

打开文件读写

对象序列化


许多应用模式,都需要根据各自的应用采用不同的数据结构,比如操作系统需要记录谁是合法用户谁在登录;一个图书系统需要知道谁付过帐了;一个网络服务器需要掌握他的活动客户等

数据结构可以分为:正在运行的程序内存中的数据结构;一个磁盘文件中的数据结构;存在数据库中的信息结构

内存中的数据结构

数组

数组是一个定常的数据项线性集合,数组的初始化完成后,在内存中占用的空间就确定下来了,不可更改,并且数组中的数据类型是一样的。

定义格式,推荐用第一种:

type[] araryName;
type arrayName[];

数组是一种引用类型的变量,定义只是定义了一个指针,并且没有指向任何有效的内存,所以定义并不能确定数组长度,当然也可以定义和初始化同时进行

int[] count1 = new int[10];
object[] prices = new String[10];

注意,上面的object类型可以存放任意类型的对象引用而不需强制类型转换,但是从该对象取出一个Object引用时需要强制类型转换。

float[] prices; //声明一个浮点型的数组
		prices = new float[3]; //构造并分配内存或者 float[] prices = new float[3]
		prices[0] = 10.1f; // 初始化内容 还有另一种初始化形式 比如 float[] prices = {10.1f,10.2f,10.3f}
		prices[1] = 10.2f;
		prices[2] = 10.3f;
		for(int i = 0; i<prices.length; i++)
		{
			System.out.println(prices[i]);
		}
		// 另一种循环foreach,方便遍历数组和集合
		for(float price : prices)
		{
			System.out.println(price);
		}
		

数组有一个缺点,不能动态调整大小,下面看下集合

集合

集合可以存储数量不等的对象,并可以实现常用的数据结构,比如栈,队列等,一般分为四种Set,List,Map,Queue,其中List和Set都是序列,Set代表无序,不可重复的集合;List代表有序,重复的集合;Map代表有映射关系的集合,存储了键值对,也可叫做哈希“,”字典“;Queue代表队列集合。

集合一般实现了很多操作,比如add(),remove(),size(),isEmpty(),contains()等

在介绍具体集合之前先看下迭代器,迭代器适用于遍历结构化数据用的,常用的有

for( init; test; change)上面数组使用了;

foreach(Type var : Interable<Type>)Interable可以是一个数组(上面数组也有例子),或者实现Iterable的任何对象,这种方式最常用;

while 循环,通常与Enumeration或Iterator结合使用

Iterator<Mydata> interator =...
while (iterator.hasNext())    {
    MyData md = iterator.next();
}

Iterable.forEach方法(Java8新增)

最新的迭代技术,这种方法可以由任何Iterable对象调用(可惜的是数组没有实现Iterable),他有一个参数,能实现功能接口java.util.function.consumer

Set

HashSet是Set接口的典型实现,大多数用这个实现类即可,他是按照hash算法来存储集合中的元素,所以有很好的存储查找性能。当向集合中添加一个元素时,会调用该对象的hashCode()方法得到该对象的hashCode值,然后根据该值决定该对象在HashSet中的存储位置,如果两个元素通过equals()方法比较返回true,但是他们的hashCode()方法返回值不相等,也会存储在不同的位置,所以HashSet集合判断两个元素相等的标准是两个对象equals()方法比较相等,并且两个对象的hashCode()方法返回值也想等

功能特点:

  • 不能保证元素的排列顺序,顺序可能与添加的顺序不同,也可能会发生改变,如果要排序的有个TreeSet类
  • 不是同步的,如果多个线程同时访问一个HashSet,假设有两个以上线程同时修改,必须通过代码保证同步
  • 元素值可以为null

List

List代表一个有序,可重复的集合。集合中每个元素都有一个顺序索引,所以可以通过索引访问指定位置的集合元素。

//List list1 = new ArrayList();// 或者使用类型安全的方式,泛型机制ArrayList<String> list1=new ArrayList<>()
		ArrayList<String> list1=new ArrayList<>();
		list1.add(new String("china")); 
		list1.add("usa"); 
		list1.add("china"); 
		list1.forEach(s -> System.out.println(s));
		for(int i=0 ; i<list1.size() ;  i++)
		{
			System.out.println(list1.get(i));
		}

此例跟前面的例子都用到了泛型的概念,否则有提示” ArrayList is a raw type. References to generic type ArrayList<E> should be       parameterized“

泛型机制,这样就以类型安全的方式将数据存储在集合中,并且从中获取数据时不必进行反向强制类型转换,类型包含在包括在尖括号中,并出现在生命和实例化集合之后,上面是声明一个ArrayList,存放String对象的引用。如果用list1.add(new Data())将不能通过编译

Map

Map用于保存具有映射关系的数据,所以保存了两组值,一组key,一组value,他们都可以是任何引用类型的数据,另外key不允许重复;

Map很实用,比如可以实现Web服务器上的一个简单的内存缓存;用于建立名字和地址的映射关系。

        Map<String,String> map1=new HashMap<>();
		map1.put("中国","亚洲"); 
		map1.put("美国","美洲"); 
		
		for(String key : map1.keySet())
		{
			System.out.println("Key:"+key+"; Value:"+map1.get(key));
		}
		System.out.println("-------查找------");
		String queryStr = "中国";
		System.out.println("Key:"+queryStr+"; Value:"+map1.get(queryStr));

Properties对象类是Hashtable类的子类,该对象在处理属性文件时使用,他可以将Map对象和属性文件关联起来,从而可以将Map对象中的key-value对写入属性文件,也可以加载文件中的key-value对;由于属性文件中的key-value都是字符串,所以对应Map的key-value都是字符串类型

import java.util.*;
import java.io.*;

public class test {
	public static void main(String []args) throws Exception {
		
		Properties props = new Properties();
		
		props.setProperty("username","user1");
		props.setProperty("passowrd","1234");
			
		props.store(new FileOutputStream("my.ini"), "config file test");
		
		Properties props1 = new Properties(); // 测试
		
		props1.setProperty("testkey1", "test value1"); // 添加属性
		props1.put("testkey2", "test value2"); // map的固有put方法添加
		//props1.load(System.in); 
		props1.load(new FileInputStream("my.ini")); // 状态ini文件中的属性
		
		System.out.println(props1);
		props1.list(System.out);				      
    }
}

Queue

Queue用于模拟队列这种数据结构,队列通常都是”先进先出“。队列插入到队尾,访问元素返回队列头部元素,通常不允许随机访问队列的元素。PriorityQueue是一个比较标准的队列实现类,他是有队列元素的顺序而不是插入顺序来加入队列的,会导致队列元素重新排序。所以在调用对应的poll是获取最小元素。还有Deque代表”双端对接“队列两头都可以添加删除元素

PriorityQueue<Integer> queue1=new PriorityQueue<>();
		queue1.offer(3); 
		queue1.offer(1); 
		queue1.offer(2); 
		queue1.offer(-1); 
		queue1.offer(4); 
		
		System.out.println(queue1);
		System.out.println(queue1.peek());// poll获取并删除元素,peek对应只取元素

 

输入输出-磁盘文件中的数据结构

大多数程序需要与外界互动,包括输入输出,输入包括读取来自磁盘,光盘,USB驱动器,DVD等存储设备上的数据、用户输入数据、甚至是网络数据;输出则刚好相反。

Java提供了许多类用于输入输出,比如打开关闭、读写文件。一般文件假定驻留在文件介质或永久存储,除特别说明,分布式文件系统也想本地磁盘文件系统一样。比如Sun的网络文件系统(NFS,UNIX和windows系统都有),SMB(windows网络文件系统,通过开源Samba程序可以用于UNIX),FUSE(用户空间的文件系统,常见UNIX系统)

Java的IO通过java.io包下的类和接口来支持,主要包括输入输出两种IO流,每一种流又包括字符流和字节流,其中字节流-(imputstream和outputstream输入输出流),字符流-Readers和Writers读写器

Java7在java.nio以其包下提供了一系列的全新的API,NIO的使用复杂,但是可以用于大规模服务端处理。

还有一个需要提下的内容就是,java对象的序列化机制可以把内存中的对象转换为二进制字节流,这样就可以吧Java对象保存在磁盘或者网络上传输。

标准输入

大多数平台支持标准输入(键盘,文件或者另一个程序的输出)和标准输出(终端窗口,打印机,磁盘上的文件或另一个程序的输入)。还有标准错误输出。一般程序在系统上启动时,标准输入,输出,错误输出三个流将被预先分配给特定的句柄或者文件描述符,所以,这些系统上的程序不需要打开任何文件或者其他特殊操作,就可以从标准输入读取数据或者将数据输出到标准输出设备,或者输出到标准错误流。在Java中,他们在System类中。在程序执行前,静态变量System.in,System.out,System.err就和操作系统的三个流相连接,因此,要从标准输入设备读取数据,需要引用变量System.in,并调用他的方法。比如从标准设备读取一个字节,并返回给一个int变量:

int i = System.in.read();

一般这些程序需要加上一些特殊声明,public static void main(String []args) throws IOException 或者加上try/catch块,否则程序编译有问题

public static void main(String []args)  {
		
		int i =0;
		try{
			i = System.in.read();
			System.out.println("Read this data:"+(char)i);// 将字节转换为字符输出
		} catch(Exception e){
			System.out.println(e);
		}      
    }

注意大多数情况是要读取多个字符或者一行(而不是上面的字节),比如中文,阿拉伯文,等一个字符对应多个字节情况。Reader的子类BufferedReader可以读取一行字符。在读取这些字符时,标准输入的System.in是一个Stream类,需要将Stream转为Readers类,刚好有一个Reader类的子类InputStreamReader就满足,

	public static void main(String []args)  {
		
		try{
			BufferedReader bs1 = new BufferedReader(new InputStreamReader(System.in));
			String inputline;
			while((inputline = bs1.readLine()) != null){
				System.out.println(inputline);
			}
			bs1.close();
		} catch(Exception e){
			System.out.println(e);
		}      

标准输出

标准输出或者标准错误输出,System.out(连接到“标准输出”的一个PrintStream打印流)和System.err是非常重要的调试工具,前面的程序已经都看的了,System.out.println,这个方法是多台的,几种不同的形式分别用于Object(调用对象的toString()方法)、String类,基本数据类型(整形,浮点型);println中的内容通常需要一个参数,这个参数是一个字符串,这个字符串可以是多个串的组合,把任何一个类型添加进去都会得到一个字符串-当然是组合后的。Syste.err类似

前面我们基本上都是用System.out,当然,可以使用Writer,PrintWriter类包含一个和PrintStream类似,并且有一个带Stream类型的构造函数,可以这样使用

	public static void main(String []args) throws IOException  {
		// 打印到标准输出
		PrintWriter pw = new PrintWriter(System.out,true);
		pw.println("Hello Gel!");
		
		// 直接写文件
		OutputStream os=new FileOutputStream("C:\\io1.txt");
		PrintWriter pw1=new PrintWriter(os);
		pw1.write("China");
		pw1.append("中国");
		pw1.println("Hello China!");
		
		pw.close();
		pw1.close();
     }

最后要注意的是,输出的串种第一个一个整形变量的问题,比如

        int i=1;
		System.out.println(i + '=' +" answer");
		System.out.println("" + i + '=' +" answer");

格式化输出Formatter和printf

前者模仿了C语言的prints,其实PrintStream和PrintWriter有一个printf函数,printf将人物委派给相应的format(),format()有一个缺省的Formatter实例

System.out.printf("%4d-%02d-%02d%n", 2019,1,1);

打开文件读写

可以构造一个FileReader,FileWriter,FileInputStream或FileOutStream等对象来操作文件

FileInputStream:

	public static void main(String []args) throws IOException  {
		// 字节输入流
		FileInputStream fis = new FileInputStream("c:\\io1.txt");
		// 创建一个长度为1024的buffer
		byte[] buf1 = new byte[1024];
		// save act read byte
		int readb=0;
		while((readb = fis.read(buf1)) > 0){
			// 取出buffer中的字节,并将字节数组转换为字符串输入
			System.out.print(new String(buf1,0,readb));
		// 打印到标准输出
		}
		fis.close();
     }

FileReader:

	public static void main(String []args) throws IOException  {
		// 字符输入流
		FileReader fis = new FileReader("c:\\io1.txt");
		// 创建一个长度为1024的buffer
		char[] buf1 = new char[1024];
		// save act read char
		int readb=0;
		while((readb = fis.read(buf1)) > 0){
			// 取出buffer中的字符,并将字节数组转换为字符串输入
			System.out.print(new String(buf1,0,readb));
		// 打印到标准输出
		}
		fis.close();
     }

FileInputStream:

	public static void main(String []args) throws IOException  {
		// 字符输入流
		FileInputStream fis = new FileInputStream("c:\\io1.txt");
		// 字节输出流
		FileOutputStream fos = new FileOutputStream("c:\\io2.txt");
		// 创建一个长度为1024的buffer
		byte[] buf1 = new byte[64];
		// save act read byte
		int readb=0;
		// 循环从输入流中取出数据
		while((readb = fis.read(buf1)) > 0){
			// 读多少,写多少
			fos.write(buf1,0,readb);
			//System.out.print(new String(buf1,0,readb));
		// 打印到标准输出
		}
		fis.close();
		fos.close();
     }

FileWriter,字符串直观,不用那么麻烦:

	public static void main(String []args) throws IOException  {
		FileWriter fw = new FileWriter("c:\\io1.txt");
		fw.write("China. \r\n");
		fw.write("USA。  \r\n");
		fw.close();
     }

对象序列化

对象序列化的目标是将对象保存在磁盘中,或允许在网络中直接传输对象。对象序列化允许吧内存中的Java对象转换成平台无关的二进制流(字节序列),进而将这种二进制流持久保存在磁盘上,通过网络将二进制流传输到另一个网络节点,而其他程序获取这种二进制流,可以将这种二进制刘恢复为原来的Java对象;序列化机制使得对象可以脱离程序的运行而单独存在;Java的很多类已经实现了序列化

对象序列化Serialize是将一个Java对象写入IO流中,反序列化Deserialize则是从IO流中恢复该Java对象

对象支持序列化的前提是对应的类是可序列化的,为了让类是序列化的,需要实现两个接口之一:

  • Serializable
  • Externalizable

序列化是RMI(Remote Method Invoke,远程方法调用,Java EE的基础)过程的参数和返回值都必须实现的机制,而RMI又是Java EE技术的基础(所有分布式应用常常需要跨平台,跨网络),所以通常,程序创建的每个JavaBean类都实现序列化

一旦实现了Serializable接口,对象就是序列化的,然后通过两步即可序列化对象

创建一个ObjectOutputStream,这个输出流是一个处理流,所以必须建立在其他节点流基础之上,比如:

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("path"));

然后调用ObjectOutputStream对象的writeObject()方法输出可序列化对象到流中

oos.writeObject(person实例);

实例化对象,写入磁盘: 

import java.io.Serializable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

class Person implements Serializable {
  private String name = "Unknown";
  private String gender = "Unknown";
  private double height = Double.NaN;

  public Person(String name, String gender, double height) {
    this.name = name;
    this.gender = gender;
    this.height = height;
  }

  @Override
  public String toString() {
    return "Name: " + this.name + ", Gender:   " + this.gender + ",  Height: "
        + this.height;
  }
}

public class test {
  public static void main(String[] args) {
    Person p1 = new Person("孙悟空", "Male", 1.7);
    Person p2 = new Person("猪八戒", "Male", 1.65);

    File fileObject = new File("c:\\person.ser");

    try ( // 创建一个ObjectOutputStream输出流
    		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
        fileObject))) {

    	// 将p1,p2对象写入输出流
      oos.writeObject(p1);
      oos.writeObject(p2);

      oos.close();
      // Display the serialized objects on the standard output
      System.out.println(p1);
      System.out.println(p2);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

反序列化,第一步一样,第二步,调用ObjectInputStream对象的readObject()方法读取流中的对象,该方法返回一个Object类型的java对象,需要强制转换为真实类型。

Person p = (Person)ois.readObject();

对应例子:

import java.io.Serializable;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;

class Person implements Serializable {
	  private String name = "Unknown";
	  private String gender = "Unknown";
	  private double height = Double.NaN;

	  public Person(String name, String gender, double height) {
	    this.name = name;
	    this.gender = gender;
	    this.height = height;
	  }

	  @Override
	  public String toString() {
	    return "Name: " + this.name + ", Gender:   " + this.gender + ",  Height: "
	        + this.height;
	  }
	}
public class test {
  public static void main(String[] args) {
    File fileObject = new File("c:\\person.ser");

    try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
        fileObject))) {

      Person p1 = (Person) ois.readObject();
      Person p2 = (Person) ois.readObject();

      System.out.println(p1);
      System.out.println(p2);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
发布了522 篇原创文章 · 获赞 87 · 访问量 75万+

猜你喜欢

转载自blog.csdn.net/jc_benben/article/details/77162921