搜尋

首頁  >  問答  >  主體

java - 一道优化的小代码题目, 面试题

问题:

  1. Snack类的isExpired方法实现了什么功能?

  2. 现有相当大量的snack对象(如一个长度100万的Snack对象数组)需要执行isExpired方法,执行时候发现效率低下, 请分析原因, 并给出优化方案?

为了方便交流学习, 我把完整的题目都贴出来了, 我主要的问题是第二问, 大家有没有好的办法?

代码如下:

public class Snack { 
    
    public Snack(String name, Date expirDate){ 
        this.name = name;
        this.expireDate = expireDate;
    }
    
    private String name;
    private Date expireDate;
    
    public boolean isExpired(){
        Date now = new Date();
        return now.compareTo(this.expireDate) > 0 ;
    }

}
巴扎黑巴扎黑2768 天前841

全部回覆(9)我來回復

  • 天蓬老师

    天蓬老师2017-04-18 09:59:04

    謝邀。

    1. 這個可以百度。當前日期如果大於該物件的expireDate就回傳true。

    2. 我的演算法不是很6,如果有誤請輕噴:先考慮物件們的expireDate是否有順序。有序可以藉鏡二分查找的思路。例如從小到大,那麼如果可以找到某個對象,那麼後面的物件全部回傳true。

    回覆
    0
  • 高洛峰

    高洛峰2017-04-18 09:59:04

    首先,你可以先看看Date对象的compareTo方法的内部实现,会有clone操作,所以導致開銷增大。

    1、isExpired方法的作用是判断当前对象是否已过期,如果expireDate在目前伺服器時間之前,則認為未過期,否則認為已過期;

    現有相當大量的snack物件(如一個長度100萬的Snack物件陣列)需要執行isExpired方法

    2、由於這個表達有點歧義,我分兩種情況來說吧:
    2.1、一個長度為100W的Snack數組,遍歷執行isExpired方法,那題目考察的可能是JVM內存管理和垃圾回收機制,因為程式會isExpired方法,那题目考察的可能是JVM内存管理和垃圾回收机制,因为程序会串行执行这100W个对象的isExpired方法(假设忽略创建这些对象的开销),而每运行一次就会new一个Date对象,而compareTo内部又会对this.expireDate进行clone操作,所以开销会比较大;
    2.2、在一定时间段内有大量的Snack对象并行执行isExpired串列

    執行這100W個物件的isExpired方法(假設忽略創建這些物件的開銷),而每運行一次就會new一個Date< /code>對象,而compareTo內部又會對this.expireDate進行clone操作,所以開銷會比較大;

    2.2、在一定時間內有大量的Snack對象

    並行

    執行isExpired方法,那題目側重考察的可能就是高並發處理,如果也能扯上2.1的知識點那可以加分;
    long或者int(精度要求不高的情况下)存储expireDate,如果是2.1,那可以把Date now = new Date()摞到方法外部(假设对时间的精度要求不是十分高),然后isExperied方法内部只需要比较now.getTime()expireDate的值即可;如果是2.2且应用部署在集群环境下,那就不能在isExpired里面生成Date個人覺得2.2的幾率大一點。
    談談我對這段程式碼的最佳化理解:1、用

    物件了,因為就算伺服器之間有做時間同步,也是有可能發生時間不一致的情況的,例如A機器與B機器相差1秒這種情況也是有可能的。這時候可以用一個全域的時間產生器,然後應用呼叫這個生成器取得目前的伺服器時間去做比較;

    2、從業務上確認

    過期時間的精確度🎜,是[年|月|日|時|分|秒|毫秒]?,不同精度的儲存和對比策略也是可以不同的,精度越高,成本越高。 🎜 🎜寫得比較亂。 🎜

    回覆
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-18 09:59:04

    如果是大量數據,每個在 isExpired() 中都去取一下时间,会比较耗时。
    由于是在同一时刻顺序调用数组中每个对象的 isExpired(),可以假设是在同一时间进行比较,那么给 isExpired() 加個重載

    public boolean isExpired(long now) {
        return now > this.expireDate.getTime();
    }

    在呼叫時可以這樣

    long now = System.currentTimeMillis();
    for (int i = 0; i < all.length; i++) {
        all[i].isExpired(now);
    }

    回覆
    0
  • 高洛峰

    高洛峰2017-04-18 09:59:04

    System.currentTimeMillis()比時間

    回覆
    0
  • 天蓬老师

    天蓬老师2017-04-18 09:59:04

    雷雷

    回覆
    0
  • 高洛峰

    高洛峰2017-04-18 09:59:04

    把數組改成優先權隊列,每次只需對堆頂元素執行isExpired(),性能從O(n)提高到O(1)

    回覆
    0
  • 天蓬老师

    天蓬老师2017-04-18 09:59:04

    現在看到什麼並行啊,我就想用Java8的新API,並行流,哈哈,所以簡單實踐了一下,僅供參考

    1. 採用Date傳統的compareTo方式比較

    2. 採用Date的System.currentTimeMillis() > expiredDate.getTime()方式比較

    每一種比較方式執行5遍,方便比較,同時每種方式採用3種模式去執行

    1. for迴圈執行模式

    2. 流循環執行模式

    3. 並行流循環執行模式

    程式碼類似這樣:

    Snack[] arr = LongStream.iterate(0, a -> a+1).limit(1000000l).mapToObj(Snack::new).toArray(Snack[]::new);
    Snack1[] arr1 = LongStream.iterate(0, a -> a+1).limit(1000000l).mapToObj(Snack1::new).toArray(Snack1[]::new);
    
            System.out.println("采用Date类compareTo进行时间比较的方式");
            for (int j = 0; j<5; j++){
    
                // for循环方式
                System.out.print("for循环方式-第" + j + "次耗时:" + forLoop(arr) + " ");
    
                // 流循环方式
                System.out.print("流循环方式-第" + j + "次耗时:" + streamLoop(arr) + " ");
    
                // 并行流循环方式
                System.out.println("并行流循环方式-第" + j + "次耗时:" + streamParallelLoop(arr));
            }
    
            System.out.println("采用Date类time进行时间比较的方式");
            for (int j = 0; j<5; j++){
    
                // for循环方式
                System.out.print("for循环方式-第" + j + "次耗时:" + forLoop(arr1) + " ");
    
                // 流循环方式
                System.out.print("流循环方式-第" + j + "次耗时:" + streamLoop(arr1) + " ");
    
                // 并行流循环方式
                System.out.println("并行流循环方式-第" + j + "次耗时:" + streamParallelLoop(arr1));
            }
    
        /**
         * for循环方式
         * @param arr
         * @return
         */
        private static long forLoop(ISnack[] arr){
            LocalDateTime begin = LocalDateTime.now();
            for (int i = 0; i<arr.length; i++){
                arr[i].isExpired();
            }
            LocalDateTime end = LocalDateTime.now();
            return begin.until(end, ChronoUnit.MILLIS);
        }
    
        /**
         * 流循环方式
         * @param arr
         * @return
         */
        private static long streamLoop(ISnack[] arr){
            LocalDateTime begin = LocalDateTime.now();
            Arrays.stream(arr).forEach(ISnack::isExpired);
            LocalDateTime end = LocalDateTime.now();
            return begin.until(end, ChronoUnit.MILLIS);
        }
    
        /**
         * 并行流循环方式
         * @param arr
         * @return
         */
        private static long streamParallelLoop(ISnack[] arr){
            LocalDateTime begin = LocalDateTime.now();
            Arrays.stream(arr).parallel().forEach(ISnack::isExpired);
            LocalDateTime end = LocalDateTime.now();
            return begin.until(end, ChronoUnit.MILLIS);
        }
        
        

    最後執行結果如下:
    100w量級

    ][2]

    1000w量級

    綜上:感覺改不改那個時間比較方式,從測試程式碼來看,影響不大(也可能跟具體實際環境有關),但是採用了並行流的方式後,執行效率確實提高了,在大數量操作的陣列或集合的時候,並行流方式處理較好,JDK會根據具體環境來決定更好並發模式

    回覆
    0
  • 迷茫

    迷茫2017-04-18 09:59:04

    最佳化有兩點
    1:Date物件的建立
    2.compareTo()方法

    回覆
    0
  • 大家讲道理

    大家讲道理2017-04-18 09:59:04

    問題中都說了「現有相當大量的snack物件(如一個長度100萬的Snack物件數組)需要執行isExpired方法,執行時候發現效率低下」

    這裡邊有兩個核心問題,不知道要解決哪個。

    1. 要解決大量的物件?
      2。 要解決執行效率低下。

    問題1,
    把這1萬個物件的陣列清空。就沒有那麼多要執行了。

    問題2,
    把這個 isExpire() 方法內執行的程式碼刪除掉,這個方法什麼都不乾,執行效率就高了。

    回覆
    0
  • 取消回覆