Java工具: 一個類型安全的 WeakArray


有的地方要用一個簡單的數組緩存運算結果,空間換時間,但是要求萬一內存不夠,要這些結果能被釋放(WeakReference),所以有了這個工具類。


/*
 * Copyright 2012 raistlic ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.lang.ref.WeakReference;
import java.util.Iterator;

/**
 * This class simply encapsulates an array to be used as a cache, which holds
 * weak references.
 * 
 * It is intended to store "not so important" contents, which can be GCed
 * when required.
 * 
 * The iterator of this class can be used to iterate it, or remove elements 
 * from it, removing elements using the iterator will not effect the subsequent
 * elements' positions.
 *
 * @author raistlic
 * @date   27/09/2012
 */
public class WeakArray<E> implements Iterable<E> {
  
  @SuppressWarnings({"unchecked", "rawtypes"})
  private WeakReference<E>[] data;
  public WeakArray(int size) {
    
    if( size < 0 )
      throw new IllegalArgumentException("Invalid array size: " + size);
    
    data = (WeakReference<E>[])new WeakReference[size];
  }
  
  public int length() {
    
    return data.length;
  }
  
  public void set(int index, E element) {
    
    // potentially throws ArrayIndexOutOfBoundsException
    if( get(index) != element )
      data[index] = new WeakReference<E>(element);
  }
  
  public E get(int index) {
    
    // potentially throws ArrayIndexOutOfBoundsException
    WeakReference<E> ref = data[index];
    return ref == null ? null : ref.get();
  }
  
  public void remove(int index) {
    
    // potentially throws ArrayIndexOutOfBoundsException
    data[index] = null;
  }

  @Override
  public Iterator<E> iterator() {
    
    return new IndexIterator();
  }
  
  private class IndexIterator implements Iterator<E> {
    
    private int index = 0;

    @Override
    public boolean hasNext() {
      
      return index < length();
    }

    @Override
    public E next() {
      
      // potentially throws ArrayIndexOutOfBoundsException
      E result = get(index);
      index++;
      return result;
    }

    @Override
    public void remove() {
      
      // potentially throws ArrayIndexOutOfBoundsException
      WeakArray.this.remove(index);
      index++;
    }
  }
}

上一篇文章裏用來計算階乘的工具類 Calculator,可以使用 WeakArray 來cache運算結果,修改如下:

/*
 * Copyright 2012 raistlic ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.math.BigInteger;

/**
 * This class is intended to provide convenient facilities for 
 * frequently used maths calculations that {@code java.lang.Math} 
 * does not cover.
 * 
 * @date   08/08/2012
 * @author raistlic
 */
public final class Calculator {
  
  private Calculator() {}
  
  private static final WeakArray<BigInteger> FACT_POOL = 
          new WeakArray<BigInteger>(1024);
  public static BigInteger factorial(int number) {

    if( number < 0 )
      throw new IllegalArgumentException();

    BigInteger result = null;

    if( number < FACT_POOL.length() )
      result = FACT_POOL.get(number);

    if( result == null ) {
      
      int lastCachedIndex = lastCachedFactIndex(number);
      if( lastCachedIndex > 0 )
        result = FACT_POOL.get(lastCachedIndex);
      if( result == null )
        result = BigInteger.ONE;
      
      for(int i = Math.max(2, lastCachedIndex+1); i <= number; i++) {
        
        result = result.multiply(BigInteger.valueOf(i));
        if( i < FACT_POOL.length() )
          FACT_POOL.set(i, result);
      }
    }
    return result;
  }
  
  private static int lastCachedFactIndex(int number) {
    
    for(int i=Math.min(number, FACT_POOL.length()-1); i>=0; i--) {
      
      if( FACT_POOL.get(i) != null )
        return i;
    }
    
    return -1;
  }
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章