/*
* Tencent is pleased to support the open source community by making
* Tencent GT (Version 2.4 and subsequent versions) available.
*
* Notwithstanding anything to the contrary herein, any previous version
* of Tencent GT shall not be subject to the license hereunder.
* All right, title, and interest, including all intellectual property rights,
* in and to the previous version of Tencent GT (including any and all copies thereof)
* shall be owned and retained by Tencent and subject to the license under the
* Tencent GT End User License Agreement (http://gt.qq.com/wp-content/EULA_EN.html).
*
* Copyright (C) 2015 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the MIT License (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://opensource.org/licenses/MIT
*
* 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.
*/
package com.tencent.wstt.gt.ui.model;
import java.util.ArrayList;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 保存历史记录的数据结构,GT版本为性能优化剥离出的新设计
* @since 2.1
*/
public class DataRecorder<T> {
private int size = 0; // 数据源的总长度
private ArrayList<ArrayList<T>> dataSet; // 数据源中分桶
private ArrayList<T> curBucket; // 当前桶,避免总从dataSet计算获取影响效率
// 锁
private ReadWriteLock lock = new ReentrantReadWriteLock();
// 单个存储列表的长度,主要为性能考虑,需要是2的n次方,且方便位移计算
private static final int CAPACITY = 4096;
public DataRecorder()
{
dataSet = new ArrayList<ArrayList<T>>();
}
/**
* @param start 起始数据序号
* @param end 结束数据序号+1,和subList的使用方式保持一致
* @return 数据记录列表
*/
public ArrayList<T> getRecordList(int start, int end) {
if (end <= start)
{
return new ArrayList<T>(0);
}
ArrayList<T> result = new ArrayList<T>(end - start);
int startBucketSeq = start / CAPACITY; // start所在桶的序号
int startLocal = start % CAPACITY; // start在桶中的位置
int endBucketSeq = (end - 1) / CAPACITY; // end所在桶的序号
int endLocal = (end - 1) % CAPACITY; // end在桶中的位置
if (startBucketSeq == endBucketSeq)
{
ArrayList<T> bucket = dataSet.get(startBucketSeq);
lock.readLock().lock();
result.addAll(bucket.subList(startLocal, endLocal + 1));
lock.readLock().unlock();
}
else
{
// 加第一桶
ArrayList<T> startBucket = dataSet.get(startBucketSeq);
lock.readLock().lock();
result.addAll(startBucket.subList(startLocal, startBucket.size() - 1));
lock.readLock().unlock();
// 加中间的桶
for (int i = startBucketSeq + 1; i < endBucketSeq; i++)
{
result.addAll(dataSet.get(i));
}
// 加最后的桶
ArrayList<T> endBucket = dataSet.get(endBucketSeq);
lock.readLock().lock();
result.addAll(endBucket.subList(0, endLocal + 1));
lock.readLock().unlock();
}
return result;
}
public ArrayList<T> getRecordList() {
ArrayList<T> result = new ArrayList<T>();
for (ArrayList<T> list : dataSet)
{
lock.readLock().lock();
result.addAll(list);
lock.readLock().unlock();
}
return result;
}
public T getRecord(int seq) {
if (seq > size)
{
return null;
}
int bucketSeq = seq / CAPACITY; // 所在桶的序号
int local = seq % CAPACITY; // 在桶中的位置
return dataSet.get(bucketSeq).get(local);
}
public void add(T entry) {
lock.writeLock().lock();
int local = size % CAPACITY; // 新加的entry如在桶中位置应该在的位置
if (local == 0)
{
// 新桶第一个记录
curBucket = new ArrayList<T>();
curBucket.add(entry);
dataSet.add(curBucket);
}
else
{
// 可以在当前桶中存放
curBucket.add(entry);
}
size++;
lock.writeLock().unlock();
}
// FIXME 加锁可能更安全一些
public int size() {
return size;
}
public void clear() {
lock.writeLock().lock();
for (ArrayList<T> list : dataSet)
{
list.clear();
}
dataSet.clear();
curBucket = null;
size = 0;
lock.writeLock().unlock();
}
}