跳至主要內容

Java类集框架

wangdx大约 41 分钟

Java 类集框架

Java 类集框架简介

如果说现在要想在程序中描述“多”个对象的概念,那么一般的习惯性的选择就是通过数组的形式来进行处理,但是数组本身会存在有一个严重的问题:数组的长度是固定的,不可修改,正是由于这样的原因,所以在实际项目开发过程里面会基于链表数据结构实现数组数据的存储。

可是需要清楚的问题在于,如果要想进行链表结构的。

如果要想开发出一个性能高,但是又可以满足各个环境下使用的链表是非常繁琐的,因为这里面存在有如下的几个问题:

  • 在链表之中如果要进行数据的存储,为了避免过多的循环所造成的时间复杂度的攀升,应该进行节点的引用处理,每 -次都要保存最后一个的节点;
  • 在链表数据进行查询的时候,由于所有的数据采用的全部都是顺序式结构,所以时间复杂度为“O(n)”
  • 开发的链表应该符合于行业的规范,但是问题是,谁来做这个规范?
  • 链表数据的存储还需要进行合理规划才可以提高最终的数据检索性能。

所以为了解决一系列的关于“动态数组”的设计问题,从 JDK1.2 之后开始在 Java 中正式引入了 Java 类集框架,这个开发框架完美的帮助用户实现了链表、数组结构、二叉树、队列、栈等信息。

提示

提示:关于各种数据结构的性能问题。 如果要是从实际的开发角度来讲可以同时实现多个数据存储的结构有三种:

  • 数组:属于 Java 语言的原始实现,其根据索引实现数据査询的时候时间复杂度为“O(n)”,而如果要进行数据查询的时候,利用二分查找算法可以实现时间复杂度为“O(log2N)”;
  • 链表:根据索引查询的时间复杂度为“O(n)”,如果一个设计良好的链表,可以基于跳表机制将时间复杂度设置为。 “O(log2N)”(对数的执行性能就是最佳的数据查找方案);
  • 树:平衡二叉树、根据 key 实现查询的时候可以迅速进行定位,其时间复杂度设置为“O(log2N)”,开发难度太高;

Collection 接口

按照所有类使用设计的环境要求来讲,只要是公共的程序编写往往都是针对于一组标准的开发,所以在 java.uil 包中就针对于类集提供有一个最为原始的开发接口:Collection,首先来观察这个接口的定义:

public interface Collection<E> extends Iterable<E>{}

提示

提示:在 JDK1.2 的时候 Java 没有提供泛型这一概念,所以 collection 接口定义的时候所能够保存的数据类型全部为 object,但是在 JDK1.5 之后追加了泛型的支持,这样的好处是可以避免不同对象存储。1

在早期的 Collection 接口里面就是一个独立的接口,其本身也不存在有任何的继承结构,但是在 JDK 1.5 之后让 Colection 接口多实现了一个 Iterable 接口,在 Collection 接口里面提供有如下的公共类集数据的操作方法:

Colection 接口并不是无限制的进行数据的存储,它可以存储最大的数据个数为“Integer.MAX VALUE”(整型最大值),但是需要说明的是,在现代的开发过程里面实际上很少能够直接见到 Collection 接口应用。

提示

解释:为什么不直接使用 Collection 接口? 在早期的 Java 开发的时代来讲,最为常用的集合接口都是 Collection,这一点在 EJB 技术里面最为常见的:后来微软为了推广其.NET 平台,所以针对于 Java 的一个社区开源项目进行了性能的评测;再之后为了明确的描述不同数据存储的区别,所以都会使用 Collection 子接口操作。

List 集合

详情
public class Test {
    public static void main(String[] args) {
        List all = List.of("www.yootk.com", "edu.yootk.com", "edu.yootk.com", "www.yootk.com", "yootk.ke.qq.com");
        System.out.println(all);
        for (Object obj : all.toArray()) {
            System.out.println("【沐言科技】" + obj);
        }
    }
}

以上的 List 集合从严格意义上来讲是属于一种半成品的工具类,如果此时执行了以下的程序代码的操作,那么会出现异常:

List all = List.of("www.yootk.com","edu.yootk.com","edu.yootk.com","www.yootk.com""yootk.ke.qq.com");
all.add("沐言优拓");

此时抛出的异常类型为“UnsupportedOperationException”指的是操作未实现异常,因为 of()方法仅仅能够创建一个固定大小的 List 实例,而真正的开发之中所要使用的 List 集合一般都有具体的子类:ArrayList、Vector、LinkedList 三个常用子类。

ArrayList

之所以这样的实现,主要的原因在于 List 接口下可能有许多的子类,这样就会存在有一些公共方法的实现,利用抽象类来实现公共方法,各个子类只完成自己特殊的方法即可,同时为了强调 ArayList 是 List 接口子类,就在 ArayList 定义的时候多实现了一次 List 接口,按照接口的使用原则来讲,如果要想接口对象实例化,则必须依靠子类的构造方法,那么在 ArayList 类里面就存在有两个构造方法。

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        List<String> all = new ArrayList<>() ; // 获取了List接口对象
        System.out.printf("【集合未保存数据前的状态】集合长度:%s、是否为空:%s\n", all.size(), all.isEmpty());
        all.add("www.yootk.com") ;
        all.add("edu.yootk.com") ;
        all.add("yootk.ke.qq.com") ;
        System.out.printf("【集合保存数据后的状态】集合长度:%s、是否为空:%s\n", all.size(), all.isEmpty());
        System.out.println(all); // 调用默认的toString(),将集合数据变为数组再输出
        for (Object obj : all.toArray()) {
            System.out.println(obj);
        }
    }
}


public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        List<String> all = new ArrayList<>() ; // 获取了List接口对象
        for (int x = 0 ; x < 500 ; x ++) {
            all.add("李兴华编程训练营:yootk.ke.qq.com") ;
        }
        System.out.println(all);
    }
}

以上的程序实现了一个最为基础的 ArayLis 处理操作,在整个的操作过程之中利用其内部给出的重要的几个操作方法实现了多个字符串对象数据的处理,同时记住,如果此时要保存的数据很多,实际上 ArrayList 也是可以实现功能的。

通过以上的源代码分析,可以发现 ArrayList 类内部所提供的基本的工作原理如下:·

  • 在 ArrayList 类中实现数据存储的时候所采用的数据类型为 Obiect 对象数组,ArrayList 是基于数组实现的集合操作;
    • 使用数组操作可以降低时间复杂度 get()方法直接定位,时间复杂度为“O(1)”,但是有长度局限,意味着就需要在项目实现的过程之中不断的进行数组长度的更新,一旦更新长度,则就需要不断的创建新的数组:
  • 通过代码的分析后可以发现在 ArayList 里面即便现在使用了无参构造(数组为空数组),那么也可以实现数据内容的“无限制”存储,而在进行数据增加的时候就需要动态的实现数组内容的扩充,扩充就意味着垃圾空间的产生。

结论:ArrayList 是 List 常用的子类,在其使用之前最佳的做法是要预估保存数据的最大长度,因为只有预估正确的长度才可以避免垃圾的产生以及不断的引用修改所带来的性能问题,ArrayList 最佳的优势在于对于数据索引查询时,可以保证其性能很高。每一次扩充为当前容量 50%;

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
    public static void main(String[] args) throws Exception {
        int oldCapacity = 10 ;
        int minCapacity = 11 ;
        int newCapacity = newLength(oldCapacity,
                minCapacity - oldCapacity, /* minimum growth */
                oldCapacity >> 1           /* preferred growth */);
        System.out.println(newCapacity);
    }
    public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
        // assert oldLength >= 0
        // assert minGrowth > 0

        int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
        if (newLength - MAX_ARRAY_LENGTH <= 0) {
            return newLength;
        }
        return hugeLength(oldLength, minGrowth);
    }
    private static int hugeLength(int oldLength, int minGrowth) {
        int minLength = oldLength + minGrowth;
        if (minLength < 0) { // overflow
            throw new OutOfMemoryError("Required array length too large");
        }
        if (minLength <= MAX_ARRAY_LENGTH) {
            return MAX_ARRAY_LENGTH;
        }
        return Integer.MAX_VALUE;
    }
}

保存自定义对象

详情
class Book {
    private String title ;
    private String author ;
    private double price ;
    public Book(String title, String author, double price) {
        this.title = title ;
        this.author = author ;
        this.price = price ;
    }
    public String toString() {
        return "【Book】图书名称:" + this.title + "、作者:" + this.author + "、价格:" + this.price + "\n" ;
    }
}
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        List<Book> all = new ArrayList<>() ; // 获取了List接口对象
        all.add(new Book("Java从入门到项目实战", "李兴华", 99.8)) ;
        all.add(new Book("Python从入门到项目实战", "李兴华", 97.9)) ;
        all.add(new Book("Go从入门到项目实战", "李兴华", 98.6)) ;
        System.out.println(all);
    }
}

此时已经成功的实现了自定义对象的存储,但是在 List 接口里面提供有一个 contains()方法、remove()方法,这个时候会发现这两个操作方法在自定义类对象面前的时候竟然无法生效。 解决办法

详情
class Book{
  ...
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true ;
        }
        if (!(obj instanceof Book)) {
            return false ;
        }
        Book book = (Book) obj ;
        return this.title.equals(book.title) && this.author.equals(book.author) && this.price == book.price ;
    }
  ...
}

LinkedList 子类

在 List 接口中除了使用 ArrayList 之外实际上还会存在有一个 LinkedList 子类,这个类与 ArrayList 类实现最大的区别在于,此类是基于链表实现的数据存储,链表在实现数据存储的时候如果是没有经过任何处理的环境下,其时间复杂度为“O(n)”,首先来观察一下 LinkedList 子类的定义,

LinedList 虽然是 List 接口的子类,但是其并没有直接去继承 AbstractList 抽象类,而是直接继承了“AbstractSequentialList”父 于是继续来观察“AbstractSequentialList”类的定义结构:

Vector

在使用 ArrayList 的时候如果通过无参构造方法实例化了 ArrayList 对象,它默认的容量大小是一个空数组,而 Vector 默认的容量大小数组长度为 10,而与 ArrayList 最大的区别就是在于其使用了 synchronized 进行了方法的实现,这个时候就可以总结出 ArrayList 与 Vector 之间的关系了:

  • ArrayList 和 Vector 都使用了数组的机制实现了数据的存储,默认开辟的数组大小是不同的,ArayList 为空数组,Vectoi 默认的大小为 10 个元素的长度;
  • ArrayList 类的全部方法为异步处理,Vector 类的全部方法使用了 synchronized 同步标记,所以 ArrayList 属于非线程安全的集合类,而 Vector 属于线程安全的集合类;
  • ArrayList 是在 JDK1.2 的时候提供的,而 Vector 是在 JDK 1.0 的时候提供的。

Set 集合

在 Java 类集设计的过程之中 Set 集合的最大特点是里面所保存的数据不允许出现有重复,Set 接口的定义如下:

public interface Set<E>extends Collection<E>{}

Set 接口是 Collection 接口的直接子接口,但是 Set 接口并没有像 List 接口那样对 Collection 接口进行了大量的扩充,而仅仅是维持了 Collection 接口定义(这就意味着 List 接口中的 get()方法是无法在 Set 子类中使用的),同时在 JDK1.9 之后 Set 接口里面也扩充了一些 default 方法:

 public static <E> Set<E> of(E... elements)
详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        // 此时的Set集合存在有重复的数据的配置
        Set<String> all = Set.of("www.yootk.com", "www.yootk.com", "edu.yootk.com");
        System.out.println(all);
    }
}

如果在使用 Set 的时候设置有重复的数据内容则会抛出"IIlegalArgumentException”异常信息,同时也会明确的告诉用户那些 数据是重复的内容

在集合接口中所给出的的 o80 方法本身仅仅只是可以实现一个集合的定义,而要想真正的进行集合内容的操作却是不支持的, 所以正确的做法应该通过其常用的子类:HashSet(散列存储)、TreeSet(有序存储)、LinkedHashSet(链表存储)。

HashSet

HashSet 的实现结构严重依赖于 HashMap 类的处理结构,所以这个时候出现的所谓的大小的关系,本质上来说与数组没有任何的直接联系。

所有的类集都是通过子类向父接口进行对象实例化,完成之后通过父接口里面定义的方法实现相关数据操作,但是对应于 HashSet 类提供有一个 LinkedHashSet 类,这个类不允许保存重复数据,但是可以按照数据保存的顺序进行存储。

详情

public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Set<String> all = new HashSet<>() ;
        all.add("www.yootk.com") ;
        all.add("www.yootk.com") ; // 重复内容,不保存
        all.add("edu.yootk.com") ;
        all.add("yootk.ke.qq.com") ;
        System.out.println(all);
    }
}

TreeSet

在日后的项目开发过程之中,如果需要实现多个数据的排序存储,比较简单的方案就是通过 TreeSet 子类来完成。

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Set<String> all = new TreeSet<>() ;
        all.add("www.yootk.com") ;
        all.add("edu.yootk.com") ;
        all.add("yootk.ke.qq.com") ;
        System.out.println(all);
    }
}

排序说明

通过之前的分析可以发现 TreeSet 中的数据都是可以进行排序处理的,那么既然可以实现字符串对象的排序,那么理论上也就就可以实现任意的数据排序操作,但是这个时候自定义类中必须要实现 Comparable 比较器接口,如果没有实现此接口则在程序执行的时候就会出现“java.lang.ClassCastException’。

要想使用 TreeSet 实现多个对象的排序,则对于类的要求实在是太高了,所以如果真的是自定义的类非必要的情况下建议慎重选择是否去使用 TreeSet

详情
class Book implements Comparable<Book> {    // 自定义类
    private String title ;
    private String author ;
    private double price ;
    public Book(String title, String author, double price) {
        this.title = title ;
        this.author = author ;
        this.price = price ;
    }
    public String toString() {
        return "【Book】名称:" + this.title + "、作者:" + this.author + "、价格:" + this.price + "\n" ;
    }
    @Override
    public int compareTo(Book o) {
        if (this.price > o.price) {
            return 1 ;
        } else if (this.price < o.price) {
            return -1 ;
        } else {
            if (this.title.compareTo(o.title) > 0) {
                return 1 ;
            } else if (this.title.compareTo(o.title) < 0) {
                return -1 ;
            } else {
                return this.author.compareTo(o.author) ;
            }
        }
    }
}
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Set<Book> all = new TreeSet<>() ;
        all.add(new Book("Java从入门到项目实战", "李兴华", 99.8)) ;
        all.add(new Book("Java从入门到项目实战", "李兴华", 99.8)) ;    // 数据重复
        all.add(new Book("Vue.JS从入门到项目实战", "李兴华", 99.8)) ;    // 数据重复
        all.add(new Book("Python从入门到项目实战", "李兴华", 98.9)) ;
        all.add(new Book("GO从入门到项目实战", "李兴华", 96.6)) ;
        System.out.println(all);
    }
}
@Override
    public int compareTo(Book o) {
        if (this.price > o.price) {
            return 1 ;
        } else if (this.price < o.price) {
            return -1 ;
        } else {
            if (this.title.compareTo(o.title) > 0) {
                return 1 ;
            } else if (this.title.compareTo(o.title) < 0) {
                return -1 ;
            } else {
                return this.author.compareTo(o.author) ;
            }
        }
    }

重复元素判断

在整个的 Set 集合里面其有一个最为重要的特点就是不允许保存有重复元素,并且 HashSet、LinkedHashSet、Treeset 都严格按照这个标准进行的类的结构设计,通过之前的程序可以发现 TreeSet 可以通过 Comparable 接口来实现重复元素的判断,但是这个判断是基于比较的方式完成的,并不是公共的重复元素判断标准。

在实际项目的开发过程之中,如果要想实现重复元素的判断要结合 Obiect 类来完成,需要通过两个方法来实现判断:

  • 获取对象编码:public int hashCode(),首先依据编码来获取数据;
  • 对象比较:public boolean equals(Objectobj),将获取的数据和传入的数据进行比对。
详情
class Book{    // 自定义类
    private String title ;
    private String author ;
    private double price ;
    public Book(String title, String author, double price) {
        this.title = title ;
        this.author = author ;
        this.price = price ;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return Double.compare(book.price, price) == 0 &&
                title.equals(book.title) &&
                author.equals(book.author);
    }
    @Override
    public int hashCode() {
        return Objects.hash(title, author, price);
    }
    public String toString() {
        return "【Book】名称:" + this.title + "、作者:" + this.author + "、价格:" + this.price + "\n" ;
    }
}
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Set<Book> all = new HashSet<>() ;
        all.add(new Book("Java从入门到项目实战", "李兴华", 99.8)) ;
        all.add(new Book("Java从入门到项目实战", "李兴华", 99.8)) ;    // 数据重复
        all.add(new Book("Vue.JS从入门到项目实战", "李兴华", 99.8)) ;    // 数据重复
        all.add(new Book("Python从入门到项目实战", "李兴华", 98.9)) ;
        all.add(new Book("GO从入门到项目实战", "李兴华", 96.6)) ;
        System.out.println(all);
    }
}

集合输出

现在为止虽然已经成功的单值集合的数据存储操作,但是在现在为止所见到的集合输出都使用的是对象数组转换后的形式完成,那么这样的输出一定不是设计开发的首选,按照 Java 类集的设计要求来讲,对于集合的输出结构一共分为四种:Iterator(90%)、Listlterator(0.1%)、Enumeration(0.9%)、foreach(9%)。

Iterator

Iterator 接口是在 JDK 1.2 的时候提供的专门用于类集数据输出的标准化操作接口,按照传统的类集的设计来讲,只要是进行集合内容的获取,那么就需要通过 lterator 来完成,但是需要注意的是,在 JDK1.5 之后以及 JDK1.8 之后,java 都有了许多新的语法的出现,所以对于集合的输出现在的操作形式也是更加丰富的。

在 JDK 1.5 版本里面专门提供了一个 Iterable 操作接口,这个接口有一个最为重要的方法就是获取 Iterator 接口对象,同时在 JDK1.8 的版本里面,Iterable 接口又得到了一些功能上的扩充。

详情
class Test1 {
    public static void main(String[] args) {
        List<String> all = new ArrayList<>() ;
        all.add("www.yootk.com") ;
        all.add("edu.yootk.com") ;
        all.add("yootk.ke.qq.com") ;
        all.forEach(System.out::println);
    }
}

实际上所谓迭代概念非常好理解,就是里面的执行会根据具体的判断结果来进行推断,例如:一个人吃饭,吃一口肯定不饱,那么这个时候一般的做法是:吃一口,饱否?没有饱继续吃。

详情
class Test2 {
    public static void main(String[] args) {
        List<String> all = new ArrayList<>() ;
        all.add("www.yootk.com") ;
        all.add("edu.yootk.com") ;
        all.add("yootk.ke.qq.com") ;
        Iterator<String> iterator = all.iterator();
        while (iterator.hasNext()){
            String str = iterator.next();
            System.out.println(str);
        }
    }
}
 // 直接使用Arrays类中提供的asList()方法将若干数据内容转为List接口实例
List<String> all = Arrays.asList("www.yootk.com", "edu.yootk.com", "yootk.ke.qq.com") ;

以上的操作为在实际项目开发过程之中最为常见的 lteartor 接口的使用形式,但是需要注意的是,在 List 接口和 lterator 接口里面都提供有一个 remove()数据删除方法,那么请问,这两个方法有什么区别?。

详情
class Test3 {
    public static void main(String[] args) {
        List<String> all = new ArrayList<>() ;
        all.add("www.yootk.com") ;
        all.add("edu.yootk.com") ;
        all.add("yootk.ke.qq.com") ;
        Iterator<String> iterator = all.iterator();
        while (iterator.hasNext()){
            String str = iterator.next();
            if ("www.yootk.com".equals(str)) {  // 设置一个判断条件
                all.remove(str) ; // 删除数据
            }
            System.out.println(str);
        }
    }
}

此时出现有一个“ConcurentModifcationException”(并发修改问题)的异常,因为在整个的集合里面,都会有一个防止多线程访问时数据修改不准确的计数变量,因为在进行迭代输出的时候,如果修改了原始的集合内容,那么对于整个的程序来讲就认为有线程破坏了正常的输出结构,所以产生了如上的问题。

但是需要给所有小伙伴们一个明确说明的事情在于:Iterator 接口主要的功能是进行输出,至于删除的功能,个人觉得适当还是应该放一放。

ListIterator

详情
class Test4 {
    public static void main(String[] args) {
        List<String> all = new ArrayList<>() ;
        all.add("www.yootk.com") ;
        all.add("edu.yootk.com") ;
        all.add("yootk.ke.qq.com") ;
        ListIterator<String> iter = all.listIterator() ;
        System.out.print("【由前向后输出】");
        while (iter.hasNext()) { // 判断是否有下一个数据
            String str = iter.next() ; // 获取数据
            System.out.print(str + "、");
            iter.set("沐言科技:" + str); // 修改数据
        }
        System.out.print("\n【由后向前输出】"); // 追加一个空行
        while (iter.hasPrevious()) {
            System.out.print(iter.previous() + "、");
        }
    }
}

虽然这个时候实现了双向迭代处理,但是从实际的开发来讲,99%的集合输出操作采用的方式大多都是单向迭代。

Enumeration

在 JDK1.0 的时候提供有了一个 Vector 向量操作类用于实现动态对象数组,但是在 JDK 1.2 之后这个类的结构已经发生了改变同时也加入到了类集之中,但是 Vector 作为早期的一种集合类,其本身也配置有一个专属的输出,那么这个输出就是 Enumeration,如果要想获得 Enuemration 接口对象实例,就必须依靠 Vector 类中的如下方法:

详情

public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Vector<String> all = new Vector<>() ; // 只有Vector才可以获取Enumeration
        all.add("www.yootk.com") ;
        all.add("edu.yootk.com") ;
        all.add("yootk.ke.qq.com") ;
        Enumeration<String> enumeration = all.elements() ; // 将Vector转为Enumeration
        while (enumeration.hasMoreElements()) { // 是否有数据?
            System.out.println(enumeration.nextElement());
        }
    }
}

foreach

虽然 foreach 的输出功能非常简化,而且相对于【erator 来讲功能也更加的强大,但是千万不要忽略 Iterator 接口的使用,而且千万要记住一个非常核心的概念:foreach 循环结构是不可能脱离掉 Iterable 接口的。

详情
class Book implements Iterable<Book> {
    private static final Book INSTANCE = new Book();
    private static final Book ALL[] = new Book[]{new Book("Java从入门到项目实战"),
            new Book("Python从入门到项目实战"), new Book("GO从入门到项目实战")};
    private int foot = 0; // 作为数组内容是否存在的计数
    private String title;

    private Book() {
    }

    private Book(String title) {
        this.title = title;
    }

    public String toString() {
        return "【Book】图书名称:" + this.title;
    }

    public static Book getInstance() {
        return INSTANCE;
    }

    @Override
    public Iterator<Book> iterator() {
        return new BookIter();
    }

    private class BookIter implements Iterator<Book> {
        @Override
        public boolean hasNext() {
            return Book.this.foot < Book.ALL.length; // 有内容
        }

        @Override
        public Book next() {
            return Book.ALL[Book.this.foot++]; // 返回当前内容
        }
    }
}

class Test6 {
    public static void main(String[] args) {
        Book book = Book.getInstance() ; // 获取Itearble接口子类实例
        for (Book temp : book) {
            System.out.println(temp);
        }
    }
}

最佳做法

详情
interface ILink<T> extends Iterable<T> {
    public void add(T e) ; // 向链表中实现数据追加
}
class LinkImpl<T> implements ILink<T> {
    private class Node {    // 要提供有一个节点
        private T data ; // 节点数据
        private Node next ; // 保存下一个节点
        public Node(T data) {   // 创建新的节点
            this.data = data ;
        }
    }
    // -------------------- 编写链表实现类 -------------------------
    private Node root ; // 根节点
    private Node last ; // 保存最后一个节点
    private Node currentNode ; // 获取数据的时候进行当前节点保存
    @Override
    public void add(T e) {
        Node newNode = new Node(e) ; // 新元素创建一个新节点
        if (this.root == null) {
            this.root = newNode ; // 第一个节点作为根元素
        } else {
            this.last.next = newNode ; // 设置下一个的引用
        }
        this.last = newNode ; // 保存最后一个节点
    }
    @Override
    public Iterator<T> iterator() {
        this.currentNode = this.root ; // 配置当前节点
        return new LinkIter(); // 返回自定义迭代对象
    }
    class LinkIter implements Iterator<T> {
        @Override
        public boolean hasNext() {
            return LinkImpl.this.currentNode != null; // 当前节点有数据
        }
        @Override
        public T next() {
            T data = LinkImpl.this.currentNode.data ; // 获取节点数据
            LinkImpl.this.currentNode = LinkImpl.this.currentNode.next ; // 修改当前节点
            return data;
        }
    }
}

class Test7 {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        ILink<String> all = new LinkImpl<>() ;
        all.add("www.yootk.com");
        all.add("edu.yootk.com");
        all.add("yootk.ke.qq.com");
        for (String data : all) {   // 直接迭代
            System.out.println(data);
        }
    }
}

Map 集合

在整个 Java 类集开发框架之中,实际上会针对于数据存储结构的不同,分为两种类集形式:单值集合、二元偶对象集合,在之前所学习到了 Collection 集合,实际上它所描述的就是一个单值集合的操作接口,所谓的二元偶对象它所描述的是值“key=value”存储结构,在使用的时候二元偶对象的主要应用就是通过 key 查找到与之对应的 value 数据。

提示

总结:对于现在给定的两种数据集合的选择。 不管使用那种数据集合都可以实现数据的存储,但是存储数据的目的是不同的。Collection(List、Set)存储数据的目的是为了进行输出,而 Map 存储数据的目的一是为了输出,二是为了数据的査询(根据 key 实现 value 的查询)。

在 JDK1.9 之后为了方便 Map 接口的使用,专门提供了许多的 of0 方法,利用此方法可以方便的创建一个没有重复 key 的 Map

详情
public class Test {
    public static void main(String[] args) {
        // 根据Map接口提供的of()方法实现Map集合的创建
        Map<Integer, String> map = Map.of(1, "one", 2, "two", 3, "three");
        System.out.println(map.getClass());
        System.out.println(map);
    }
}

此时由于出现有重复的 key 的信息,这样一来整个的程序代码就会产生异常,即:Map 集合中是不建议大家过多的使用重复数据进行配置处理的。

HashMap

HashMap 是整个 Map 接口之中最为常用的一个子类,同时也是在很多实际面试的过程里面最为常见的面试问题,首先来观察下 HashMap 的基本定义:

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Map<Integer, String> map = new HashMap<>(); // 通过子类实现接口对象实例化
        System.out.println("【未发生替换】" + map.put(1, "edu.yootk.com"));
        System.out.println("【已发生替换】" + map.put(1, "www.yootk.com")); // key重复
        System.out.println("【未发生替换】" + map.put(2, "yootk.ke.qq.com")); // 新的key
        System.out.println(map);
    }
}

以上的 Map 集合设置了具体的数据信息,观察到了 put()方法返回值的作用,但是对于 HashMap 子类来讲有一个比较重要的特点:在 HashMap 子类中出现的 key 或者是 value 都是允许为 null 的。

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Map<Integer, String> map = new HashMap<>(); // 通过子类实现接口对象实例化
        map.put(null, "两手空空") ; // key为null
        System.out.println("【未发生替换】" + map.put(1, "edu.yootk.com"));
        System.out.println("【已发生替换】" + map.put(1, "www.yootk.com")); // key重复
        System.out.println("【未发生替换】" + map.put(2, "yootk.ke.qq.com")); // 新的key
        map.put(0, null) ; // value为null
        System.out.println(map);
    }
}

通过此时的执行结果可以清楚的发现,在通过 HashMap 子类实现数据存储的时候里面所保存的数据都是无序的状态,所以只要跟 Hash 有关的程序类都属于无序存储数据,需要清楚的是之所以向 Map 集合中存储数据,最为重要的意义是在于可以根据 key 实现相关数据的查找。

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Map<Integer, String> map = new HashMap<>(); // 通过子类实现接口对象实例化
        map.put(null, "两手空空") ; // key为null
        map.put(99, "edu.yootk.com") ;
        map.put(22, "yootk.ke.qq.com") ;
        map.put(33, "www.yootk.com") ;
        map.put(0, null) ; // value为null
        System.out.println("【key不存在】" + map.get(9));     // 返回null
        System.out.println("【key存在】" + map.get(33));    // 返回具体内容
        System.out.println("【null-key查询】" + map.get(null)); // 查询null的内容
    }
}

通过以上的执行代码已经可以掌握 Map 接口的主要作用了,但是这里面有一个比较重要的问题就是在于 HashMap 子类的工作原理的实现机制,下面针对于这个 HashMap 子类的工作原理进行分析。

2 在 HashMap 子类里面如果要想进行数据査询则一定要进行数据的保存,而数据的保存需要通过 put()方法来完成,来分析一下 put()方法对应的源代码以及其相关的操作方法,

通过分析可以得出结论,每当 HashMap 中的存储容量达到了阈值范围,那么将自动实现扩容 1 倍的处理操作,所有的操作为 了保证性能都是直接通过移位的形式来处理的。

3 性能保障:通过之前讲解过的数据链表以及二叉树的结构对比可以发现,链表在进行数据査询的时候其时间复杂度为“O(n)”,3、而对于二叉树的结构来讲其时间复杂度为“O(log2n)”,但是如果此时数据量较大的情况下,就必须保证二叉树的平衡,所以为了解决 HashMap 中的性能的平衡操作,从 JK1.8 开始 HashMap 设计结构上发生了转变,为了保证査询性能,在 HashMap 中如果发现数据量较大的情况下会自动实现一个红黑树的结构转换,那么这个转换的进行在 HashMap 里面是有完整定义的:。

由于为了保证性能必须将 HashMap 中的存储结构进行转换,所以在进行转换的时候就需要采用“treeifyBin()”方法利用排序节点类实现数据的存储,这样就可以通过红黑树的结构实现 Map 数据的存储,以到达二叉树的平衡,从而保证性能始终维持在 “O(log2N)”

LinkHashMap

HashMap 在进行数据存储的时候实际上是不会考虑到数据的保存顺序的,它整个的数据存储都是无序状态,因为在保存的过程之中都需要根据 key 来实现 Hash 值的计算,但是如果要是希望现在可以按照一定的顺序进行数据的保存,最佳的做法就可以通过 LinkedHashMap 类来实现,此类的定义结构如下:

详情
class Test1 {
    public static void main(String[] args) {
        Map<Integer, String> map = new LinkedHashMap<>(); // 通过子类实现接口对象实例化
        map.put(null, "两手空空") ; // key为null
        map.put(99, "edu.yootk.com") ;
        map.put(22, "yootk.ke.qq.com") ;
        map.put(33, "www.yootk.com") ;
        map.put(0, null) ; // value为null
        System.out.println(map);
    }
}

TreeMap

java.util.TreeMap 子类是一个拥有排序功能的 Map 接口子类,所有保存的数据会按照 key 所给出的自然顺序进行排序处理。

Hashtable

Hashtable 的子类是在 JDK 10 的时候提供给开发者使用的,属于比较早期的一种功能类,而到了 JDK 1.2 之后由于类集框架 的设计,所以又重新对 Hashtable 子类进行了改进,这样才有了 Hashtable 的延用,首先来观察一下 Hashtable 的定义:.

HashMap 与 Hashtable

总结:请解释 HashMap 与 Hashtable 的区别?

  • HashMap 是在 JDK 1.2 的时候提供的工具类,而 Hashtable 是在 JDK 1.0 的时候提供的工具类;
  • HashMap 在进行数据存储的时候其所保存的 key 和 value 都可以为 nul,而 Hashtable 不能保存空,否则会出现空指向;
  • Hashtable 默认的初始化的容量为 11 个元素,而 HashMap 的初始化容量为 16 个元素;
  • Hashtable 进行数据操作的时候采用的是同步处理,属于线程安全的集合类,而 HashMap 采用的是异步处理,属于非线程安全的集合类。。

Map.Entry

Map.Entry 是 Map 中提供的一个内部接口,这个内部接口的基本定义如下:。

public static interface Map.Entry<K,V> {}

在之前讲解 Colection 接口和 Map 接口的时候都曾经观察过这两个接口中常用子类的源代码,那么下面做一个内部实现节点的对比(不管是链表还是二叉树,实际上内部的数据存储都是需要有一个节点的)

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Map.Entry<String, String> entry = Map.entry("YOOTK", "www.yootk.com");
        System.out.println("key = " + entry.getKey() + "、value = " + entry.getValue());
    }
}

早先的 JDK 版本里面如果要想操作 Map.Entry 接口那么一般都是在 Map 集合输出的时候使用,但是从 JDK1.9 开始在 Map 接里面定义有一个 static 方法:

public static <K,V> Map.Entry<K,V> entry(K k, V v)

只要通过 entryO 方法设置有相关的 key 和 value 就能够自动创建一个 Map.Entry 接口实例。

Iterator

通过整个接口关系的结构就可以非常清楚的发现如下的输出操作的流程:

  • 利用 Map 接口中提供的 entrySet()方法将 Map 集合转为 Set 集合,其中 Set 集合中的每一个数据都是一个节点,但是这个节点都属于“Map.Entry”接口实例;
  • 当获取了 Set 接口对象实例之后就可以通过 iterator() 方法来获取 Iterator 接口对象实例:。
  • 获取了 Iterator 接口实例之后就可以利用 while 循环实现集合的迭代操作,每一次迭代出来的对象都是 Map.Entry;
  • 利用 Map.Entry 接口中提供的 getKey()、getValue()方法获取包装的 key 与 value 数据。
详情
class Test2 {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("YOOTK", "www.yootk.com");  // 保存数据
        map.put("VIP", "yootk.ke.qq.com");  // 保存数据
        Set<Map.Entry<String,String>> set = map.entrySet();
        Iterator<Map.Entry<String,String>> iterator = set.iterator();
        while (iterator.hasNext()){
            Map.Entry<String,String> entry = iterator.next();
            System.out.println("key = " + entry.getKey() + "、value = " + entry.getValue());
        }
    }
}

以上的处理就是 Map 集合迭代输出的标准做法,但是需要注意的是,实际上现在的输出也可以考虑通过 foreach 循环的形式完成,但是依然需要通过 entrySet()方法将 Map 集合转为 Set 集合。

详情
class Test3 {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("YOOTK", "www.yootk.com");  // 保存数据
        map.put("VIP", "yootk.ke.qq.com");  // 保存数据
        for (Map.Entry<String,String> entry:map.entrySet()){
            System.out.println("key = " + entry.getKey() + "、value = " + entry.getValue());
        }
    }
}

所有的 foreach 输出的处理操作都与 Iterable 接口密不可分,而 Map 接口并没有继承 Iterable 接口,所以此时就必须利用 entrySet()将 Map 集合转为 Set 集合,随后才可以实现最终内容的迭代输出。.提示:对于 Map 集合的输出还有一些比较重要的提示,就是关于错误的输出。 在 Map 接口中存在有一个 keySet0 方法,这个方法可以将 Map 集合中全部的 key 取出,取出的全部的 key 会以一个 Set 集合的形式返回,随后通过每一个获取到的 key 再去查询对应的 value。

详情
class Test4 {
    public static void main(String[] args) {
        /**
         * 错误的map输出
         */
        Map<String, String> map = new HashMap<>();
        map.put("YOOTK", "www.yootk.com");  // 保存数据
        map.put("VIP", "yootk.ke.qq.com");  // 保存数据
        Set<String> keys = map.keySet() ; // 获取全部的key集合
        for (String key : keys) {
            System.out.println("key = " + key + "、value = " + map.get(key)); // 查询key对应的value
        }
    }
}

在之前如果通过 Map.Entry 获取全部 Map 集合的时候实际上仅仅是迭代了一次 Map 中的全部数据,但是如果说现在采用了如上的方式来进行处理,这个时候实际上的时间复杂度为“O(N)(全部迭代一次) + 0(l0g2N)(每一次根据 key 査找 value)”,那么如果说现在的 Map 集合中的数据非常的多,此时可以预计到最终的执行性能是很差的。

详情

class Book {
    private String title ;
    public Book(String title) {
        this.title = title ;
    }
}
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Map<Book, String> map = new HashMap<>() ; // 此时作为key的类型为一个自定义类
        map.put(new Book("Java从入门到项目实战"), "Java小红书") ;
        System.out.println(map.get(new Book("Java从入门到项目实战")));
    }
}

自定义 Map 的 KEy

在使用 Map 集合的时候,实际上任意的数据类型都可以成为 key 的类型,那么这个作为 key 的类一定要在类中覆写 Object 中 的 hashCode()与 equals()两个方法,因为所谓的 key 查找数据实际上就需要进行对象重复的判断。

详情
class Book {
    private String title ;
    public Book(String title) {
        this.title = title ;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return title.equals(book.title);
    }
    @Override
    public int hashCode() {
        return Objects.hash(title);
    }
}

需要提醒所有读者的是,虽然在 Java 里面允许用户创建属于自己的 key 的类型,但是考虑到实际的项目开发来说,一般作为 Map 中的 key 类型的只有三个类型:String、 Integer、Long.

问题:在进行 Map 数据存储的时候,如果 Hash 值重复了该怎么办呢?

可以发现在进行 putVal()方法调用的时候会根据传入的 key 来实现一个 hash 码的生成,这个生成利用的是 hash0 方法完成的,而这个 hash()方法其内部主要就是基于 hashCode() 方法来实现的,如果此时最终生成的哈希码的内容完全相同,那么在数据结构之中将其称为“哈希冲突”。

面对“哈希冲突”在软件设计之中有四种解决方案:开放定址法、链地址法、建立公共溢出区、再哈希法,在 Java 中是利用链地址法的形式解决的哈希冲突,这种操作的主要机制是将重复的 Hash 值的内容保存在一个链表之中。

Stack 栈

在数据结构之中所谓的栈结构所描述的就是一种先进后出数据结构,例如:在浏览器的操作界面存在有“后退”和“前进”的按钮,每当选择“后退”操作实际上都是将最近一次的页面重新展示,而最早打开的页面一直要回退到最后才能见到。又再例如:手机上有一个返回按钮,如果此时你打开的界面很多,那么每一次的返回都是返回上一个界面,这些操作实际上描述的就是栈。

在 Java 类集里面如果要想实现栈的功能,则必须依 Stack 类来完成, 此类的定义如

只要是栈的数据结构一般都会有两种重要的状态:空栈(栈中没有任何数据),满栈(栈溢出),当栈中有数据的时候才可以弹出对应的数据,弹出的时候就顺道将数据删除了。

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Stack<String> stack = new Stack<>() ; // 定义栈
        System.out.println("【是否为空栈】" + stack.empty());
        stack.push("沐言优拓:www.yootk.com") ;
        stack.push("VIP课程大纲:edu.yootk.com") ;
        stack.push("李兴华编程训练营:yootk.ke.qq.com") ;
        while (!stack.empty()) {    // 如果栈中有数据
            System.out.println("【栈顶弹出数据】" + stack.pop());
        }
    }
}

栈的功能属于先进后出(FILO、First Input Last Output),在早期如果要想实现字符串的反转操作一般都会有一种思路:字符串通过字符数组来进行描述,随后通过入栈和出栈的形式实现反转,

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        String str = "www.yootk.com" ; // 一个字符串
        char data [] = str.toCharArray() ; // 将字符串转为字符数组
        Stack<Character> stack = new Stack<>() ;
        for (int x = 0; x < data.length; x++) {
            stack.push(data[x]) ; // 入栈
        }
        while (!stack.empty()) {
            System.out.print(stack.pop());
        }
    }
}

但是还有一点一定要特别注意,如果此时的栈已经是空栈了,那么这个时候如果再次出栈则会产生空栈异常。

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Stack<String> stack = new Stack<>() ; // 定义栈
        stack.push("沐言优拓:www.yootk.com") ;
        System.out.println("【第一次弹出】" + stack.pop());
        System.out.println("【第二次弹出】" + stack.pop());
    }
}

Queue 队列

Stack 是一种栈的处理形式,所有的栈操作结构采用的全部都是“FILO”的模式(先进后出的模式),但是一些时候可能需要进行先进先出的模式(FirstImput First Output、FIFO),而这种操作就需要通过队列来实现了。

在队列中提供的两组方法最大的区别在于,一个在执行队列处理操作的时候有可能产生异常,另外一个是不会产生异常的,但是如果有一些特殊的情况会返回一些特殊的内容。

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Queue<String> queue = new LinkedList<>(); // 为队列的接口对象实例化
        System.out.println("【向队列增加数据“www.yootk.com”】" + queue.offer("www.yootk.com"));
        System.out.println("【向队列增加数据“edu.yootk.com”】" + queue.offer("edu.yootk.com"));
        System.out.println("【向队列增加数据“yootk.ke.qq.com”】" + queue.offer("yootk.ke.qq.com"));
        while (!queue.isEmpty()) {   // 如果不是空队列
            System.out.println("【通过队列获取数据】" + queue.poll());
        }
    }
}


详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Deque<String> deque = new LinkedList<>(); // 为队列的接口对象实例化
        deque.addFirst("【FIRST】www.yootk.com");
        deque.addLast("【LAST】edu.yootk.com");
        System.out.println(deque.pollLast());
        System.out.println(deque.pollLast());
    }
}

Properties 属性操作

在应用程序设计与开发的过程之中是存在有一种国际化的设计概念的,利用国际化的方式可以使得同样的应用程序以不同语言的方式进行执行,而在整个国际化实现的过程之中提供有一个“*.properties”资源文件(属性文件),而现在所给出的 Properties 类主要的功能就可以方便的实现这些资源文件的处理操作。

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        // 如果此时实例化Properties属性的时候没有设置初始化大小,则容量为8
        Properties properties = new Properties() ; // 创建一个属性操作类
        properties.setProperty("YOOTK", "沐言优拓:www.yootk.com") ;
        properties.setProperty("EDU", "VIP课程大纲:edu.yootk.com") ;
        properties.setProperty("VIP", "李兴华编程训练营:yootk.ke.qq.com") ;
        System.out.println("【获取属性KEY存在】" + properties.getProperty("YOOTK"));
        System.out.println("【获取属性KEY不存在】" + properties.getProperty("MUYAN"));
        System.out.println("【获取属性KEY不存在】" + properties.getProperty("MUYAN", "暂未启用"));
    }
}

public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        // 如果此时实例化Properties属性的时候没有设置初始化大小,则容量为8
        Properties properties = new Properties(); // 创建一个属性操作类
        properties.setProperty("YOOTK", "沐言优拓:www.yootk.com");
        properties.setProperty("EDU", "VIP课程大纲:edu.yootk.com");
        properties.setProperty("VIP", "李兴华编程训练营:yootk.ke.qq.com");
        properties.store(new FileOutputStream(new File("H:" + File.separator + "MuYan.properties")), "MuYan-Info");
    }
}

在 JDK1.8 及以前的所有的版本里面,如果要想在资源文件之中保存中文的数据信息,那么必须进行转码处理,此时一般都使用 JDK 内部所提供的一个工具:native2ascii.exe,但是这个工具在 JDK1.9 之后默认不提供了。

此时的程序既然已经可以通过 Properties 类实现属性文件的存储,那么也就可以通过 Properties 类实现属性文件资源的读取。此时也需要注意一个问题:对于资源文件读取就有了两种方式,一种是通过 Properties 类完成读取,另外一种是通过 ResourceBundle 类读取的,这两种读取我个人偏向于使用 ResourceBundle,因为这个类有一个最大的特点在于可以方便的读取 CLASSPATH 下的资源文件,按照包的形式进行读取控制。.

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        // 如果此时实例化Properties属性的时候没有设置初始化大小,则容量为8
        Properties properties = new Properties(); // 创建一个属性操作类
        properties.load(new FileInputStream(new File("H:" + File.separator + "MuYan.properties")));
        System.out.println("【获取属性信息】" + properties.getProperty("YOOTK"));
    }
}

Collections 工具类

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        List<String> all = new ArrayList<>();
        Collections.addAll(all, "www.yootk.com", "edu.yootk.com", "yootk.ke.qq.com");
        System.out.println("【原始集合】" + all);
        Collections.reverse(all); // 集合反转
        System.out.println("【集合反转】" + all);
        Collections.sort(all); // 集合排序
        System.out.println("【集合排序】" + all);
    }
}

在正常的情况下来讲只有 Vector 类可以实现枚举的输出,但是如果说现在有了 Collections,任何的 Colection 接口子类都可以通过 Enumeration 实现输出。

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        List<String> all = new ArrayList<>();
        Collections.addAll(all, "www.yootk.com", "edu.yootk.com", "yootk.ke.qq.com");
        Enumeration<String> enu = Collections.enumeration(all) ;
        while (enu.hasMoreElements()) {
            System.out.println(enu.nextElement());
        }
    }
}

Stream

从 JDK1.8 开始提供有了 iava.uti.stream 数据流的开发包,而 Stream 就是这个包中所提供的一个接口,这个接口最主要的目的就是通过函数式编程的结构实现集合数据的分析,所以在 Colection 接口中也在 JDK18 版本后追加了相关的操作方法:

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        List<String> data = new ArrayList<>() ; // 要分析的数据存储
        // 现在假设在集合之中要存储的数据有成千上万个
        Collections.addAll(data, "Java", "JavaScript", "Python", "Ruby", "GO", "C", "C++", "Sql") ;
        Stream<String> stream = data.stream() ; // 获取Stream对象
        // filter():对给定的数据执行过滤,本次是查询是否包含有单词“java”
        // 将所有满足过滤条件的数据收集成一个新的List集合
        List list = stream.filter((ele) -> ele.toLowerCase().contains("java")).collect(Collectors.toList()) ;
        System.out.println(list);
    }
}

如果此时的操作没有采用如上的函数式的编程方式来完成,则这样的操作代码需要手工获取 Iterator 迭代对象,随后编写 i 语句,同时还需要定义一个新的 List 集合进行数据的存储,而如果使用了函数式编程一行语句解决了所有的设计问题,而后在 Stream 进行数据处理的时候还有一个比较重要的功能,可以实现数据的部分操作,需要两个方法的支持。。

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        List<String> data = new ArrayList<>() ; // 要分析的数据存储
        // 现在假设在集合之中要存储的数据有成千上万个
        Collections.addAll(data, "Java", "JavaScript", "Python", "Ruby", "GO", "C", "C++", "Sql") ;
        Stream<String> stream = data.stream() ; // 获取Stream对象
        // filter():对给定的数据执行过滤,本次是查询是否包含有单词“java”
        // 将所有满足过滤条件的数据收集成一个新的List集合
        List list = stream.skip(2).limit(4).filter((ele) -> ele.toLowerCase().matches("(.*j.*)|(.*c.*)")).collect(Collectors.toList()) ;
        System.out.println(list);
    }
}
详情
class Order {   // 描述订单
    private String name; // 商品名称
    private double price; // 商品单价
    private int amount; // 数量

    public Order(String name, double price, int amount) {
        this.name = name;
        this.price = price;
        this.amount = amount;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    public int getAmount() {
        return amount;
    }
}

public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        List<Order> orders = new ArrayList<>(); // 创建订单集合
        orders.add(new Order("工程车玩具-vip版", 138.9, 500));
        orders.add(new Order("Java从入门到项目实战", 99.8, 5000));
        orders.add(new Order("VIP训练营", 13800.00, 60));
        Stream<Order> stream = orders.stream();
        DoubleSummaryStatistics vip = stream.filter((ele) -> ele.getName().toUpperCase().contains("VIP")).mapToDouble((orderObject) -> orderObject.getPrice() * orderObject.getAmount()).summaryStatistics();
        System.out.println("【订单总量】" + vip.getCount());
        System.out.println("【订单总费用】" + vip.getSum());
        System.out.println("【商品平均价格】" + vip.getAverage());
        System.out.println("【商品最高价格】" + vip.getMax());
        System.out.println("【商品最低价格】" + vip.getMin());
    }
}

类集应用案例

在所有的 Java 项目的开发过程之中,几乎都会有类集存在的身影,那么既然类集需要存在,就一定要有其存在的核心意义, 那么类集最大的意义就是动态的实现数据的保存,同时类集本身也提供有序列化的支持。

类集序列化对象存储

如果要想将对象保存在文件里面,则一定要使用到序列化的概念,那么既然需要序列化的概念,就一定要使用到 Serializable 以及 ObiectOutputStream、ObiectInputStream.

但是在当前的整个程序计算过程之中,最为重要的部分就是多个对象的存储,而多个对象的存储按照之前的学习来说肯定要通过 Collection 接口完成,于是就需要清楚关于序列化接口的定义

1、根据表结构定义出各个的简单 Java 类

详情
package com.yix.collection.case1.vo;

import java.io.Serializable;

/**
 * @author wangdx
 */
public class Privilege implements Serializable {
    private static final long serialVersionUID = -1617440024988698495L;
    private String pid;
    private String title;
    private Role role; // 权限对应的角色

    public Privilege() {
    }

    public Privilege(String pid, String title) {
        this.pid = pid;
        this.title = title;
    }

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Role getRole() {
        return role;
    }

    public void setRole(Role role) {
        this.role = role;
    }

    @Override
    public String toString() {
        return "【权限】权限ID = " + this.pid + "、名称 = " + this.title;
    }
}
package com.yix.collection.case1.vo;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * @author wangdx
 */
public class Role implements Serializable {
    private static final long serialVersionUID = -9168084466411202122L;
    private String rid;
    private String title;
    private List<Privilege> privileges; // 角色对应的多个权限
    private List<User> users; // 角色对应的多个用户

    public Role() {
        this(null, null);
    }

    public Role(String rid, String title) {
        this.rid = rid;
        this.title = title;
        this.privileges = new ArrayList<>();
        this.users = new ArrayList<>();
    }

    public String getRid() {
        return rid;
    }

    public void setRid(String rid) {
        this.rid = rid;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public List<Privilege> getPrivileges() {
        return privileges;
    }

    public void setPrivileges(List<Privilege> privileges) {
        this.privileges = privileges;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    @Override
    public String toString() {
        return "【角色】角色ID = " + this.rid + "、名称 = " + this.title;
    }
}
package com.yix.collection.case1.vo;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * @author wangdx
 */
public class User implements Serializable {

    private static final long serialVersionUID = 2901002810717366713L;
    private String uid;
    private String name;
    private List<Role> roles; // 用户对应的角色

    public User() {
        this.roles = new ArrayList<>();
    }

    public User(String uid, String name) {
        this.uid = uid;
        this.name = name;
        this.roles = new ArrayList<>();
    }

    public String getUid() {
        return uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "【用户】用户ID = " + this.uid + "、姓名 = " + this.name;
    }
}

2.接口和实现类创建

详情
package com.yix.collection.case1.service;

import java.io.File;
import java.io.IOException;

/**
 * @author wangdx
 */
public interface IDataService {
    public static final File OBJECT_FILE = new File("D:" + File.separator + "object.ser") ;
    public void store(Object object) throws IOException ; // 数据存储
    public Object load() throws IOException;
    public void showByUser() throws Exception ; // 根据用户查找信息
    public void showByRole() throws Exception ; // 根据角色查找信息
    public void showPrivilege() throws Exception ; // 根据权限查找信息
}
package com.yix.collection.case1.service.impl;

import com.yix.collection.case1.service.IDataService;
import com.yix.collection.case1.vo.Privilege;
import com.yix.collection.case1.vo.Role;
import com.yix.collection.case1.vo.User;

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

/**
 * @author wangdx
 */
public class DataServiceImpl implements IDataService {
    @Override
    public void store(Object object) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(OBJECT_FILE)) ;
        oos.writeObject(object); // 对象输出
        oos.close();
    }
    @Override
    public Object load() throws IOException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(OBJECT_FILE)) ;
        try {
            Object obj = ois.readObject() ;
            return obj ;
        } catch (ClassNotFoundException e) {
            return null ;
        } finally {
            ois.close();
        }
    }
    @Override
    public void showByUser() throws Exception {
        Map<String, Object> map = (Map<String, Object>) this.load() ; // 读取数据
        User user = (User) map.get("yootk-zs") ; // 获取用户信息
        System.out.println(user); // 获取用户信息
        for (Role role : user.getRoles()) { // 迭代用户角色
            System.out.println("\t|- " + role);
            for (Privilege privilege : role.getPrivileges()) {
                System.out.println("\t\t|- " + privilege);
            }
        }
    }

    @Override
    public void showByRole() throws Exception {
        Map<String, Object> map = (Map<String, Object>) this.load() ; // 读取数据
        Role role = (Role) map.get("user") ; // 获取指定的角色信息
        System.out.println(role);
        for (User user : role.getUsers()) {
            System.out.println("\t|- " + user);
        }
        for (Privilege privilege : role.getPrivileges()) {
            System.out.println("\t|- " + privilege);
        }
    }

    @Override
    public void showPrivilege() throws Exception {
        Map<String, Object> map = (Map<String, Object>) this.load() ; // 读取数据
        Privilege privilege = (Privilege) map.get("user:edit") ; // 获取指定的权限信息
        System.out.println(privilege);
        System.out.println("\t|- " + privilege.getRole());
        for (User user : privilege.getRole().getUsers()) {
            System.out.println("\t\t|- " + user);
        }
    }
}

3.工厂模式

详情
package com.yix.collection.case1.factory;

import com.yix.collection.case1.service.IDataService;
import com.yix.collection.case1.service.impl.DataServiceImpl;

/**
 * @author wangdx
 */
public class ServiceFactory {
    private ServiceFactory() {}
    public static IDataService getDataServiceInstance() {
        return new DataServiceImpl() ;
    }
}

4.客户端代码

详情
package com.yix.collection.case1.demo;

import com.yix.collection.case1.factory.ServiceFactory;
import com.yix.collection.case1.service.IDataService;
import com.yix.collection.case1.vo.Privilege;
import com.yix.collection.case1.vo.Role;
import com.yix.collection.case1.vo.User;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author wangdx
 */
public class YixDemo {
    private static IDataService dataService ;
    static {    // 静态代码块获取对象实例
        dataService = ServiceFactory.getDataServiceInstance() ;
        try {
            dataService.store(init()); // 将init()方法返回结果直接存储
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws Exception {
        System.out.println("----------------- 【1】根据用户查找对应信息 -----------------");
        dataService.showByUser();
        System.out.println("----------------- 【2】根据角色查找对应信息 -----------------");
        dataService.showByRole();
        System.out.println("----------------- 【3】根据权限查找对应信息 -----------------");
        dataService.showPrivilege();
    }

    public static Map<String, Object> init() {
        Map<String, Object> map = new HashMap<>();
        // 创建对象内容
        User userA = new User("yootk-zs", "张三");
        User userB = new User("yootk-ls", "李四");
        User userC = new User("yootk-ww", "王武");
        Role roleA = new Role("user", "用户管理");
        Role roleB = new Role("backup", "系统备份");
        Privilege priA = new Privilege("user:add", "用户增加");
        Privilege priB = new Privilege("user:edit", "用户修改");
        Privilege priC = new Privilege("user:delete", "用户删除");
        Privilege priD = new Privilege("user:select", "用户查询");
        Privilege priE = new Privilege("backup:all", "全量备份");
        Privilege priF = new Privilege("backup:inc", "增量备份");
        Privilege priG = new Privilege("backup:recover", "备份恢复");
        roleA.getPrivileges().add(priA) ;   // 角色与权限关联
        roleA.getPrivileges().add(priB) ;   // 角色与权限关联
        roleA.getPrivileges().add(priC) ;   // 角色与权限关联
        roleA.getPrivileges().add(priD) ;   // 角色与权限关联
        roleB.getPrivileges().add(priE) ;   // 角色与权限关联
        roleB.getPrivileges().add(priF) ;   // 角色与权限关联
        roleB.getPrivileges().add(priG) ;   // 角色与权限关联
        priA.setRole(roleA);    // 权限与角色关联
        priB.setRole(roleA);   // 权限与角色关联
        priC.setRole(roleA);   // 权限与角色关联
        priD.setRole(roleA);   // 权限与角色关联
        priE.setRole(roleB);   // 权限与角色关联
        priF.setRole(roleB);   // 权限与角色关联
        priG.setRole(roleB);   // 权限与角色关联
        userA.getRoles().add(roleA) ;   // 用户和角色关联
        userA.getRoles().add(roleB) ;  // 用户和角色关联
        userB.getRoles().add(roleA) ;  // 用户和角色关联
        userC.getRoles().add(roleB) ; // 用户和角色关联
        roleA.getUsers().add(userA) ; // 角色和用户关联
        roleA.getUsers().add(userB) ; // 角色和用户关联
        roleB.getUsers().add(userA) ; // 角色和用户关联
        roleB.getUsers().add(userC) ; // 角色和用户关联
        map.put(userA.getUid(), userA); // 保存用户信息
        map.put(userB.getUid(), userB); // 保存用户信息
        map.put(userC.getUid(), userC); // 保存用户信息
        map.put(roleA.getRid(), roleA); // 保存角色信息
        map.put(roleB.getRid(), roleB); // 保存角色信息
        map.put(priA.getPid(), priA); // 保存权限信息
        map.put(priB.getPid(), priB);// 保存权限信息
        map.put(priC.getPid(), priC);// 保存权限信息
        map.put(priD.getPid(), priD);// 保存权限信息
        map.put(priE.getPid(), priE);// 保存权限信息
        map.put(priF.getPid(), priF);// 保存权限信息
        map.put(priG.getPid(), priG);// 保存权限信息
        return map;
    }
}

部门信息查找

利用类集实现一个如下复杂结构的信息输出:一个公司中包含有多个部门信息,每个部门有多个雇员,同时每一位雇员有各自处理的业务范围。

1、根据表结构定义出各个的简单 Java 类

详情
package com.yix.collection.deptsearch.vo;

import java.io.Serializable;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * @author wangdx
 */
public class Emp implements Serializable {
    private Long empno ;
    private String ename ;
    private Dept dept ;
    private Set<String> business ; // 业务
    public Emp() {
        this(null, null) ;
    }
    public Emp(Long empno, String ename) {
        this.empno = empno ;
        this.ename = ename ;
        this.business = new LinkedHashSet<>() ; // 主要考虑添加顺序和无重复
    }

    public Long getEmpno() {
        return empno;
    }

    public void setEmpno(Long empno) {
        this.empno = empno;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public Set<String> getBusiness() {
        return business;
    }

    public void setBusiness(Set<String> business) {
        this.business = business;
    }

    @Override
    public String toString() {
        return "【Emp】雇员编号:" + this.empno + "、姓名:" + this.ename + "、业务:" + this.business ;
    }
}
package com.yix.collection.deptsearch.vo;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * @author wangdx
 */
public class Dept implements Serializable {
    private Long deptno ;
    private String name ;
    private Company company ;
    private Map<Long, Emp> emps ; // 包含雇员
    public Dept() {
        this(null, null) ;
    }
    public Dept(Long deptno, String name) {
        this.deptno = deptno ;
        this.name = name ;
        this.emps = new HashMap<>() ;
    }

    public Long getDeptno() {
        return deptno;
    }

    public void setDeptno(Long deptno) {
        this.deptno = deptno;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    public Map<Long, Emp> getEmps() {
        return emps;
    }

    public void setEmps(Map<Long, Emp> emps) {
        this.emps = emps;
    }

    @Override
    public String toString() {
        return "【Dept】部门编号:" + this.deptno + "、部门名称:" + this.name + "、部门人数:" + this.emps.size() ;
    }
}
package com.yix.collection.deptsearch.vo;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * @author wangdx
 */
public class Company implements Serializable {
    private String name ;
    private String site ;
    private Map<Long, Dept> depts ; // 包含部门
    public Company() {
        this(null, null) ;
    }
    public Company(String name, String site) {
        this.name = name ;
        this.site = site ;
        this.depts = new HashMap<>() ;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSite() {
        return site;
    }

    public void setSite(String site) {
        this.site = site;
    }

    public Map<Long, Dept> getDepts() {
        return depts;
    }

    public void setDepts(Map<Long, Dept> depts) {
        this.depts = depts;
    }

    @Override
    public String toString() {
        return "【Company】公司名称:" + this.name + "、服务站点:" + this.site ;
    }
}

2.接口和实现类创建

详情
package com.yix.collection.deptsearch.service;

import com.yix.collection.deptsearch.vo.Company;
import com.yix.collection.deptsearch.vo.Dept;
import com.yix.collection.deptsearch.vo.Emp;

/**
 * @author wangdx
 */
public interface IHRService {
    public Company init() ; // 初始化信息
    public Dept findByDept(Long deptno) ; // 根据部门编号查找部门信息
    public Emp findByEmp(Long empno) ; // 根据雇员编号查找雇员信息
    public Company findCompany() ; // 获取公司信息
}
package com.yix.collection.deptsearch.service.impl;

import com.yix.collection.deptsearch.service.IHRService;
import com.yix.collection.deptsearch.vo.Company;
import com.yix.collection.deptsearch.vo.Dept;
import com.yix.collection.deptsearch.vo.Emp;

import java.util.Arrays;
import java.util.Map;

/**
 * @author wangdx
 */
public class HRServiceImpl implements IHRService {

    private Company company ;

    @Override
    public Company init() {
        this.company = new Company("沐言科技", "www.yootk.com") ; // 获取公司实例
        Dept deptA = new Dept(10L, "技术研发部") ;   // 实例化部门数据
        Dept deptB = new Dept(20L, "教学研发部") ;  // 实例化部门数据
        Dept deptC = new Dept(30L, "售后服务部") ;  // 实例化部门数据
        deptA.setCompany(this.company); // 设置部门与公司之间的关联
        deptB.setCompany(this.company); // 设置部门与公司之间的关联
        deptC.setCompany(this.company); // 设置部门与公司之间的关联
        this.company.getDepts().put(deptA.getDeptno(), deptA) ; // 设置公司和部门之间的关联
        this.company.getDepts().put(deptB.getDeptno(), deptB) ; // 设置公司和部门之间的关联
        this.company.getDepts().put(deptC.getDeptno(), deptC) ; // 设置公司和部门之间的关联
        Emp empA = new Emp(7867L, "Leo") ;  // A部门
        Emp empB = new Emp(7962L, "Hello") ; // A部门
        Emp empC = new Emp(7369L, "Smith") ; // A部门
        Emp empD = new Emp(7566L, "Allen") ; // B部门
        Emp empE = new Emp(7579L, "Ford") ; // B部门
        Emp empF = new Emp(8919L, "King") ; // B部门
        empA.setDept(deptA); // 雇员与部门之间关联
        empB.setDept(deptA); // 雇员与部门之间关联
        empC.setDept(deptA); // 雇员与部门之间关联
        empD.setDept(deptB); // 雇员与部门之间关联
        empE.setDept(deptB); // 雇员与部门之间关联
        empF.setDept(deptB); // 雇员与部门之间关联
        deptA.getEmps().put(empA.getEmpno(), empA) ; // 部门与雇员关联
        deptA.getEmps().put(empB.getEmpno(), empB) ; // 部门与雇员关联
        deptA.getEmps().put(empC.getEmpno(), empC) ; // 部门与雇员关联
        deptB.getEmps().put(empD.getEmpno(), empD) ; // 部门与雇员关联
        deptB.getEmps().put(empE.getEmpno(), empE) ; // 部门与雇员关联
        deptB.getEmps().put(empF.getEmpno(), empF) ; // 部门与雇员关联
        empA.getBusiness().addAll(Arrays.asList("Java", "业务完善", "项目组长")); // 雇员业务
        empB.getBusiness().addAll(Arrays.asList("Java", "业务完善"));// 雇员业务
        empC.getBusiness().addAll(Arrays.asList("Java", "业务完善"));// 雇员业务
        empD.getBusiness().addAll(Arrays.asList("咨询", "VIP服务", "公开课"));// 雇员业务
        empE.getBusiness().addAll(Arrays.asList("咨询", "VIP服务"));// 雇员业务
        empF.getBusiness().addAll(Arrays.asList("咨询", "VIP服务"));// 雇员业务
        return this.company;
    }

    @Override
    public Dept findByDept(Long deptno) {
        return this.company.getDepts().get(deptno); // 通过公司的信息查询部门内容
    }

    @Override
    public Emp findByEmp(Long empno) {
        for (Map.Entry<Long, Dept> deptEntry : this.company.getDepts().entrySet()) {
            if (deptEntry.getValue().getEmps().containsKey(empno)) {
                return deptEntry.getValue().getEmps().get(empno) ;
            }
        }
        return null;
    }

    @Override
    public Company findCompany() {
        return this.company;
    }
}

3.工厂模式

详情
package com.yix.collection.deptsearch.factory;

import com.yix.collection.deptsearch.service.IHRService;
import com.yix.collection.deptsearch.service.impl.HRServiceImpl;

/**
 * @author wangdx
 */
public class ServiceFactory {
    private ServiceFactory() {}
    public static IHRService getHRServiceInstance() {
        return new HRServiceImpl() ;
    }
}

4.客户端代码

详情
package com.yix.collection.deptsearch.demo;

import com.yix.collection.deptsearch.factory.ServiceFactory;
import com.yix.collection.deptsearch.service.IHRService;
import com.yix.collection.deptsearch.vo.Company;
import com.yix.collection.deptsearch.vo.Dept;
import com.yix.collection.deptsearch.vo.Emp;

import java.util.Map;

/**
 * @author wangdx
 */
public class YixDemo {
    public static void main(String[] args) {
        IHRService hrService = ServiceFactory.getHRServiceInstance() ;
        System.out.println("------------- 【1】获取公司的完整架构 ---------------");
        Company company = hrService.init() ;
        System.out.println(company);
        for (Map.Entry<Long, Dept> deptEntry : company.getDepts().entrySet()) {
            System.out.println("\t|- " + deptEntry.getValue());
            for (Map.Entry<Long, Emp> empEntry: deptEntry.getValue().getEmps().entrySet()) {
                System.out.println("\t\t|- " + empEntry.getValue());
            }
        }
        System.out.println("------------- 【2】获取指定部门信息 ---------------");
        Dept dept = hrService.findByDept(10L) ; // 获取部门信息
        System.out.println(dept);
        for (Map.Entry<Long, Emp> empEntry: dept.getEmps().entrySet()) {
            System.out.println("\t|- " + empEntry.getValue());
        }
        System.out.println("------------- 【3】获取指定雇员信息 ---------------");
        Emp emp = hrService.findByEmp(7369L) ;
        System.out.println(emp);
    }
}

学生成绩统计

利用类集实现若干学生信息存储,除了学生的基本信息之外还要求保存学生的数学成绩、编程成绩、英语成绩,随后统计所 有学生的总成绩以及每门课程的平均成绩。 1、根据表结构定义出各个的简单 Java 类

详情
package com.yix.collection.studentscore.vo;

import java.io.Serializable;

/**
 * @author wangdx
 */
public class Student implements Serializable, Comparable<Student>{
    private String name ;
    private Integer age ;
    private Double math ; // 数学成绩
    private Double program ; // 编程成绩
    private Double english ; // 英语成绩
    public Student() {}
    public Student(String name, Integer age, Double math, Double program, Double english) {
        this.name = name;
        this.age = age;
        this.math = math;
        this.program = program;
        this.english = english;
    }
    @Override
    public int compareTo(Student o) {
        if ((this.math + this.program + this.english) > (o.math + o.program + o.english)) {
            return -1 ;
        } else if ((this.math + this.program + this.english) < (o.math + o.program + o.english)) {
            return 1 ;
        } else {
            return this.age - o.age; // 成绩相同按照年龄排序
        }
    }
    @Override
    public String toString() {
        return "【Student】姓名:" + this.name + "、年龄:" + this.age + "、数学成绩:" + this.math + "、编程成绩:" + this.program + "、英语成绩:" + this.english + "\n";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Double getMath() {
        return math;
    }

    public void setMath(Double math) {
        this.math = math;
    }

    public Double getProgram() {
        return program;
    }

    public void setProgram(Double program) {
        this.program = program;
    }

    public Double getEnglish() {
        return english;
    }

    public void setEnglish(Double english) {
        this.english = english;
    }
}

2.接口和实现类创建

详情
package com.yix.collection.studentscore.service;


import com.yix.collection.studentscore.vo.Student;

import java.util.List;
import java.util.Map;

/**
 * @author wangdx
 */
public interface IStudentService {
    public void add(Student student) ; // 保存数据到集合之中
    public Map<String, Double> getAllScore() ; // 得到每位学生的总成绩
    public Map<String, Double> getAvgScore() ; // 得到每门课程的平均成绩
    public List<Student> findAll() ; // 获取全部的学生信息
}
package com.yix.collection.studentscore.service.impl;

import com.yix.collection.studentscore.service.IStudentService;
import com.yix.collection.studentscore.util.MathUtil;
import com.yix.collection.studentscore.vo.Student;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * @author wangdx
 */
public class StudentServiceImpl implements IStudentService {
    // 无法知道到底会有多个学生学生进行存储,如果使用ArrayList,那么就面临引用垃圾过多的问题
    private List<Student> allStudents = new LinkedList<>() ;
    @Override
    public void add(Student student) {
        this.allStudents.add(student) ; // 向集合之中添加数据
    }
    @Override
    public Map<String, Double> getAllScore() {
        Map<String, Double> result = new HashMap<>() ;
        for (Student student : this.allStudents) {
            result.put(student.getName(), MathUtil.round((student.getMath() + student.getEnglish() + student.getProgram()), 2));
        }
        return result;
    }
    @Override
    public Map<String, Double> getAvgScore() {
        Map<String, Double> result = new HashMap<>() ;
        for (Student student : this.allStudents) {
            result.put(student.getName(), MathUtil.round((student.getMath() + student.getEnglish() + student.getProgram()) / 3, 2));
        }
        return result;
    }
    @Override
    public List<Student> findAll() {
        return this.allStudents;
    }
}

3.工厂模式

详情
package com.yix.collection.studentscore.factory;

import com.yix.collection.studentscore.service.IStudentService;
import com.yix.collection.studentscore.service.impl.StudentServiceImpl;

/**
 * @author wangdx
 */
public class ServiceFactory {
    private ServiceFactory() {}
    public static IStudentService getStudentServiceInstance() {
        return new StudentServiceImpl() ;
    }
}

4.客户端代码

详情
package com.yix.collection.studentscore.demo;

import com.yix.collection.studentscore.factory.ServiceFactory;
import com.yix.collection.studentscore.service.IStudentService;
import com.yix.collection.studentscore.vo.Student;

import java.util.Collections;
import java.util.List;

/**
 * @author wangdx
 */
public class YixDemo {
    public static void main(String[] args) {
        IStudentService studentService = ServiceFactory.getStudentServiceInstance() ;
        studentService.add(new Student("张三", 18, 78.9, 89.2, 91.3));
        studentService.add(new Student("李四", 19, 65.2, 39.2, 89.5));
        studentService.add(new Student("王五", 17, 89.8, 78.6, 93.3));
        System.out.println("================= 【1】、每位学生的总成绩 ================");
        System.out.println(studentService.getAllScore());
        System.out.println("================= 【2】、每位学生的平均成绩 ================");
        System.out.println(studentService.getAvgScore());
        System.out.println("================= 【3】、学习成绩大排名 ================");
        List<Student> all = studentService.findAll() ;
        Collections.sort(all); // 实现数据排序
        System.out.println(all);
    }
}
上次编辑于: