3.1 :顺序容器

容器是现代程序设计非常基础而重要的手段。

所谓容器,就是“放东西的东西”。数组可以看作是一种容器,但是数组的元素个数一旦确定就无法改变,这在实际使用中是很大的不足。一般意义上的容器,是指具有自动增长容量能力的存放数据的一种数据结构。在面向对象语言中,这种数据结构本身表达为一个对象。所以才有“放东西的东西”的说法。 Java具有丰富的容器,Java的容器具有丰富的功能和良好的性能。熟悉并能充分有效地利用好容器,是现代程序设计的基本能力。

顺序容器

  • 放进容器中的对象是按照指定的顺序(放的顺序)排列起来的,而且允许具有相同值的多个对象存在。

Notebook

我们设计一个记事本,首先明确需求

  • 能存储记录不限制能存储的记录的数量
  • 能知道已经存储的记录的数量
  • 能查看存进去的每一条记录
  • 能删除一条记录
  • 能列出所有的记录

设计记事本这个类,我们应该完成一些什么接口? 成熟的类一定是人机交互与业务逻辑分离,所以我们需要完成接口设计

  • add(String note); 实现添加功能
  • getSize(); 获取记事本的记录数量
  • getNote(int index); 通过序号获取记事本记录
  • removeNote(int index); 删除记录
  • list(); 获取所有记录
  • 定义接口

    1. public void add(String s) {
    2. // 添加note
    3. }
    4. public int getSize() {
    5. // 返回note数量
    6. return 0;
    7. }
    8. public String getNote(int index){
    9. // 通过序号返回某个note
    10. return "";
    11. }
    12. public boolean removeNote(int index) {
    13. // 可能会通过返回布尔值,表示是否删除
    14. return true;
    15. }
    16. public String[] list() {
    17. // 返回数组,仅关注数据本身与业务逻辑,后续显示交给界面显示
    18. return new String[1];
    19. }
  • 定义数据容器

  • 容器类:用来存放任意数量的对象
  • 容器类有两个类型:容器类型,元素类型

    1. //定义一个私有数组
    2. //容器类型<元素类型>
    3. private ArrayList<String> notes = new ArrayList<String>();
    4. //ArrayList范型类容器
    5. //notes :本身为一个对象管理者
  • 编写方法

    1. public void add(String s) {
    2. // 添加note
    3. notes.add(s);
    4. }
    5. public void add(String s,int location) {
    6. // 添加note到某个现有位置的前面
    7. notes.add(location, s);
    8. }
    9. public int getSize() {
    10. // 返回note数量
    11. return notes.size();
    12. }
    13. public String getNote(int index){
    14. // 通过序号返回某个note
    15. return notes.get(index);
    16. }
    17. public void removeNote(int index) {
    18. // 不用返回布尔值,直接删除
    19. notes.remove(index);
    20. }
    21. public String[] list() {
    22. // 返回数组,仅关注数据本身与业务逻辑,后续显示交给界面显示
    23. String[] a = new String[notes.size()];
    24. // for(int i=0;i<notes.size();i++)
    25. // {
    26. // a[i] = notes.get(i);
    27. // }
    28. notes.toArray(a);
    29. // 调用ArrayList自带方法
    30. return a;
    31. }
  • 编写测试main函数

    1. public static void main(String[] args) {
    2. Notebook nb = new Notebook();
    3. nb.add("first");
    4. nb.add("second");
    5. nb.add("third",1); //定位添加
    6. System.out.println(nb.getSize());
    7. System.out.println(nb.getNote(1));
    8. nb.removeNote(1);
    9. String[] b = nb.list();
    10. for(String s:b) {
    11. System.out.println(s);
    12. }
    13. //使用for-each循环输出
    14. }

思考:究竟把什么放进容器里去了?

当我们用add函数把对象放进容器中去的时候,究竟是把什么放进了容器?放进去的是对象本身吗?放进去以后,那个对象还在外面吗?如果修改了放进去的那个对象,容器中的对象会变化吗?

  • 验证代码 ```java ArrayList s = new ArrayList(); //定义s 类型为ArrayList,新建一个ArrayList交给s管理 String a =”12345”; //创建一个string类:对象a,其数据为“12345” s.add(a); System.out.println(s.get(0)); //第一次输出 System.out.println(a); //第二次输出 a = “11111”; //改变a的值 System.out.println(s.get(0)); //第三次输出 System.out.println(a); //第四次输出
  1. - 结果:可见修改a的值后,s[0]并没有随之修改
  2. ```java
  3. 12345
  4. 12345
  5. 12345
  6. 11111
  • 结论:仅仅是将对象的数据放入容器,对象作为数据的管理者,对象依然在外部。

3.2 :对象数组

当数组的元素的类型是类的时候,数组的每一个元素其实只是对象的管理者而不是对象本身。因此,仅仅创建数组并没有创建其中的每一个对象!

对象数组与普通数组

  • 定义一个int数组与String字符串数组: ```java int[] ia = new int[10]; String[] a = new String[10]; System.out.println(ia[0]); System.out.println(a[0]); System.out.println(a[0].length());

结果: 0 null “nullpointException空指针异常”

  1. - String数组的每个元素赋值
  2. ```java
  3. int[] ia = new int[10];
  4. String[] a = new String[10];
  5. for (int i=0;i<a.length;i++) {
  6. a[i]=""+i;
  7. }
  8. System.out.println(ia[0]+2);
  9. System.out.println(a[0]);
  10. System.out.println(a[0].length());
  11. 结果:
  12. 2
  13. 0
  14. 1
  • 通过上面代码,我们知道对象数组中,每个元素都是对象的管理者而非对象本身
  • int数组的元素就是数据本身
  • String数组的元素是某一字符串对象的管理者

第三周:对象容器 - 图1

对象数组使用for-each循环操作

  1. class Value{
  2. private int i;
  3. public void set(int i) {this.i = i;}
  4. public int get() {return i;}
  5. public String toString() {return ""+i;}
  6. //在Value类中添加public String toString()后
  7. }
  8. 定义一个Value
  9. public static void main(String[] args) {
  10. Value[] a = new Value[10];
  11. for(int i=0;i<a.length;i++){
  12. a[i]=new Value();
  13. a[i].set(i);
  14. } //给数组中的每个Value对象赋值
  15. for(Value v:a){
  16. System.out.println(v.get());
  17. v.set(0);
  18. }
  19. //通过for-each循环遍历并执行get方法
  20. }
  • 假如for-each循环执行到a数组的第1个元素,循环变量V这时候也指向a[0]所指的对象

也就可以通过.运算符执行Value对象的成员函数set,从而改变当前对象的值


3.3 :集合容器(set)

集合就是数学中的集合的概念:所有的元素都具有唯一的值,元素在其中没有顺序。

集合容器

  1. ArrayList<String> s = new ArrayList<String>();
  2. s.add("first");
  3. s.add("second");
  4. s.add("first");
  5. System.out.println(s);
  6. System.out.println("-----------------");
  7. HashSet<String> a = new HashSet<String>();
  8. a.add("second");
  9. a.add("first");
  10. a.add("first");
  11. a.add("second");
  12. System.out.println(a);
  13. 结果:
  14. [first, second, first]
  15. -----------------
  16. [first, second]
  • 可知SET容器与数学中的SET具有相同特性:无序,唯一

public String toString()

  • Java内置对象基本自带此方法:将输出转化为字符串输出 ```java 使用此前定义的 Value 类 public static void main(String[] args) { Value a1 = new Value(); a1.set(10); System.out.println(a1); }

输出: Value@15db9742

  1. - 因为自定义的`Value`类没有`toString()`方法,所以输出了对象管理者`a1`的地址
  2. ```java
  3. class Value{
  4. private int i;
  5. public void set(int i) {this.i = i;}
  6. public int get() {return i;}
  7. public String toString() {return ""+i;}
  8. //现在我们添加上这个方法
  9. }
  10. public static void main(String[] args) {
  11. Value a1 = new Value();
  12. a1.set(10);
  13. System.out.println(a1);
  14. }
  15. 输出:
  16. 10
  • 可见对象已自动调用toString()方法

思考:集合能用get()函数来获得某个位置上的元素吗?

因为set是一个无序的集合容器,所以不可以用get()函数来调用set容器中的某一具体位置的值


3.4 :散列表(Hash)

传统意义上的Hash表,是能以int做值,将数据存放起来的数据结构。 Java的Hash表可以以任何实现了hash()函数的类的对象做值来存放对象。 Hash表是非常有用的数据结构,熟悉它,充分使用它,往往能起到事半功倍的效果。

Hash表的应用

我们想要做一个查找硬币的程序,每个数字对应着相应的叫法,我们希望程序和具体的数据是无关的,我们需要设计一个普遍适用的程序该如何实现?

  • 抽象思维:这个程序只不过是需要接收,存储与查找数据,我们仅需要一个合理的数据存储结构与数据接口,就可以实现,这种一一对应且无序的存储结构,就可以使用散列表Hashmap。 | 1 | penny | | —- | —- | | 5 | nickel | | 10 | dime | | 25 | quanter | | 50 | half-doller |
  • Hashmap:以一对数据为单位进行存储,<键key:值Value>

  • 一个包裹类型的变量,可以直接接收它所对应的基础类型的变量:Integer类型可直接接收整数

  • 先定义私有变量coinnames与Coin类构造函数,进行coinnames数据初始化
  • 定义数据接口:getName获取对应值,add添加新数据

    1. public class Coins{
    2. private HashMap<Integer,String> coinnames = new HashMap<Integer,String>();
    3. public Coins(){
    4. coinnames.put(1,"penny");
    5. coinnames.put(10,"dime");
    6. coinnames.put(25,"quarter");
    7. coinnames.put(50,"half-dollar");
    8. coinnames.put(50,"五毛");
    9. // 具有覆盖特性,对于相同的键,值总是取最后录入的那一个
    10. }
    11. public void add(int amount,String name){
    12. Integer i = amount;
    13. coinnames.put(i,name);
    14. }
    15. //add输入函数
    16. public String getName(int amount){
    17. if(coinnames.containsKey(amount))
    18. return coinnames.get(amount);
    19. else return "莫得!";
    20. }
    21. //getName取值函数,通过键获取到对应值
  • 进行主函数数据测试

    1. public static void main(String[] args) {
    2. Scanner in = new Scanner(System.in);
    3. int amount = in.nextInt(); //输入查询数值
    4. Coins coin = new Coins(); //新建Coin对象
    5. String name = coin.getName(amount); //定义String对象接收返回的数据
    6. System.out.println(name);
    7. System.out.println(coin.coinnames);
    8. }

    可以利用for-each循环进行遍历,也可以直接输出Hashmap(自带to String函数)

    1. for (Integer k: coinnames.keySet()) {
    2. String s = coinnames.get(k);
    3. System.out.println(s);
    4. }
    5. //通过for-each遍历哈希表中的元素,通过keySet取到每个对应的键与值