java实现环形数组
Posted wen-pan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java实现环形数组相关的知识,希望对你有一定的参考价值。
如何通过java优雅的实现一个环形数组?下面提供2种实现方式
SimpleCircularArray
是环形数组简单实现CircularArrayHolder
代码实现参考的是com.netflix.hystrix.utilHystrixRollingNumber
实现方式一、SimpleCircularArray
bucket
/**
* 桶
*
* @author wenpanfeng 2022/07/30 11:17
*/
public class Bucket
/**
* 桶名称
*/
String name;
public Bucket()
public Bucket(String name)
this.name = name;
public String getName()
return name;
public void setName(String name)
this.name = name;
SimpleCircularArray
/**
* 简单的环形数组实现,非线程安全,若需保证线程安全只需要在使用时对addLast和clear方法加锁即可
*
* @author wenpanfeng 2022/07/30 13:34
*/
public class SimpleCircularArray implements Iterable<Bucket>
/**
* 环形数组,数组里是一个个的桶,桶内需要放什么数据可自己决定
*/
private final AtomicReferenceArray<Bucket> circularArray;
/**
* 头指针
*/
private int head;
/**
* 尾指针
*/
private int tail;
/**
* 当前数组内元素的个数
*/
private int size;
/**
* 环形数组容量
*/
private final int capacity;
public SimpleCircularArray(int capacity)
circularArray = new AtomicReferenceArray<>(capacity);
head = 0;
tail = 0;
this.capacity = capacity;
public SimpleCircularArray(AtomicReferenceArray<Bucket> circularArray)
this.circularArray = circularArray;
head = 0;
tail = 0;
capacity = circularArray.length();
public void addLast(Bucket bucket)
// 已经到达最后一个
if (size == capacity)
if (head == capacity - 1)
head = 0;
else
head = head + 1;
if (tail == capacity)
circularArray.set(0, bucket);
tail = 1;
else
circularArray.set(tail, bucket);
tail = tail + 1;
else
// 环形数组中元素个数还未达到capacity,则只移动tail
circularArray.set(tail, bucket);
tail = tail + 1;
size++;
/**
* 清除环形数组
*/
public void clear()
size = 0;
head = 0;
tail = 0;
/**
* 在内部数组的副本上返回一个迭代器,以便迭代器不会因同时添加删除的存储桶而失败。
*/
@Override
public Iterator<Bucket> iterator()
// 获取环形数组里的所有元素,这里获取到的是环形数组里的元素的副本
return Collections.unmodifiableList(Arrays.asList(getArray())).iterator();
/**
* 获取环形数组中所有元素
*/
protected Bucket[] getArray()
List<Bucket> array = new ArrayList<>();
// 依次获取环形数组内部所有元素并加入到新的list
for (int i = 0; i < size; i++)
array.add(circularArray.get(convert(i)));
return array.toArray(new Bucket[0]);
/**
* convert() 方法采用逻辑索引(好像 head 始终为 0)并计算 elementData 内的索引
*/
private int convert(int index)
return (index + head) % (capacity);
实现方式二、CircularArrayHolder
/**
* 环形数组管理器,通过该holder来方便的操作环形数组
*
* @author wenpanfeng 2022/07/30 11:12
*/
public class CircularArrayHolder implements Iterable<Bucket>
/**
* 持有一个环形数组的引用,以便于可以通过该引用方便的访问环形数组
*/
private final AtomicReference<CircularArray> circularArray;
/**
* 固定值,一旦创建就不会改变, 预留一个空间,作为后续向环形数组增减和删除的支持,
* 长度始终为:桶的数量 + 1,比如:如果环形数组里有10个桶,那么该值就是11
* <p>
*/
private final int dataLength;
/**
* 桶的数量
*/
private final int numBuckets;
/**
* 构造函数
*/
public CircularArrayHolder(int size)
// + 1 as extra room for the add/remove;
AtomicReferenceArray<Bucket> buckets = new AtomicReferenceArray<>(size + 1);
// state持有该环形数组的引用
circularArray = new AtomicReference<>(new CircularArray(buckets, 0, 0));
dataLength = buckets.length();
numBuckets = size;
/**
* 清除环形数组里的所有元素(线程安全)
*/
public void clear()
while (true)
// 获取到环形数组的引用
CircularArray currentCircularArray = circularArray.get();
// 调用环形数组的clear方法,此时会返回环形数组新的引用
CircularArray newCircularArray = currentCircularArray.clear();
// 使用新的引用替换旧的引用
if (circularArray.compareAndSet(currentCircularArray, newCircularArray))
// 如果cas替换成功则退出,不然则进行下一次尝试
return;
/**
* 在内部数组的副本上返回一个迭代器,以便迭代器不会因同时添加删除的存储桶而失败。
*/
@Override
public Iterator<Bucket> iterator()
// 获取环形数组里的所有元素,这里获取到的是环形数组里的元素的副本
return Collections.unmodifiableList(Arrays.asList(getArray())).iterator();
/**
* 往环形数组尾部添加一个bucket(非线程安全)
*/
public void addLast(Bucket bucket)
// 获取到环形数组的引用
CircularArray currentCircularArray = circularArray.get();
// 将元素添加到环形数组里,添加成功后会返回一个新的环形数组的引用
CircularArray newCircularArray = currentCircularArray.addBucket(bucket);
// 将circularArray重新指向最新的环形数组
circularArray.compareAndSet(currentCircularArray, newCircularArray);
/**
* 获取环形数组最后一个元素
*/
public Bucket getLast()
return peekLast();
/**
* 获取环形数组里的元素个数
*/
public int size()
// 大小也可以每次计算为: return (tail + data.length() - head) % data.length();
return circularArray.get().size;
/**
* 获取环形数组最后一个元素
*/
public Bucket peekLast()
return circularArray.get().tail();
/**
* 获取环形数组中所有的元素
*/
private Bucket[] getArray()
return circularArray.get().getArray();
/**
* 私有内部类,不允许外部直接访问(环形数组实现类,适用于写多读少的场景)
*/
private class CircularArray
/**
* 环形数组,数组里是一个个的桶,桶内需要放什么数据可自己决定
*/
protected final AtomicReferenceArray<Bucket> data;
/**
* 环形数组的大小(也就是环形数组中现有元素的个数)
*/
protected final int size;
/**
* 数组头节点下标索引
*/
protected final int tail;
/**
* 数组尾节点下标索引
*/
protected final int head;
/**
* 构造方法
*/
public CircularArray(AtomicReferenceArray<Bucket> data, int head, int tail)
this.data = data;
this.head = head;
this.tail = tail;
// 计算size
if (head == 0 && tail == 0)
size = 0;
else
size = (tail + dataLength - head) % dataLength;
/**
* 获取环形数组尾部元素
*/
public Bucket tail()
// 桶内还没有元素时,返回null
if (size == 0)
return null;
else
// we want to get the last item, so size()-1
return data.get(convert(size - 1));
/**
* convert() 方法采用逻辑索引(好像 head 始终为 0)并计算 elementData 内的索引
*/
private int convert(int index)
return (index + head) % dataLength;
/**
* 获取环形数组中所有的元素
*/
protected Bucket[] getArray()
List<Bucket> array = new ArrayList<>();
// 依次获取环形数组内部所有元素并加入到新的list
for (int i = 0; i < size; i++)
array.add(data.get(convert(i)));
return array.toArray(new Bucket[0]);
/**
* 增加一个元素到环形数组尾部
*/
private CircularArray incrementTail()
// 如果已经到达环形数组最大长度,则头尾指针一起移动
if (size == numBuckets)
return new CircularArray(data, (head + 1) % dataLength, (tail + 1) % dataLength);
else
// 如果还没有到达环形数组最大容量,则increment only tail
return new CircularArray(data, head, (tail + 1) % dataLength);
/**
* 清除环形数组,其实也就是新建一个CircularArray然后将头尾指针都指向0位置
*
* @return CircularArray
* @author wenpanfeng 2022/7/28 21:31
*/
public CircularArray clear()
return new CircularArray(new AtomicReferenceArray<>(dataLength), 0, 0);
/**
* 添加一个桶到环形数组里
*/
public CircularArray addBucket(Bucket bucket)
// 设置尾结点位置的值为bucket
data.set(tail, bucket);
// 尾部移动一个
return incrementTail();
测试
public class Main
public static void main(String[] args)
System.out.println("=============================测试CircularArrayHolder=============================");
CircularArrayHolder holder = new CircularArrayHolder(10);
for (int i = 0; i < 20; i++)
holder.addLast(new Bucket(String.valueOf(i)));
for (Bucket next : holder)
System.out.println(next.getName());
holder.clear();
System.out.println("==================================================");
for (Bucket next : holder)
System.out.println(next.getName());
System.out.println("=============================测试SimpleCircularArray=============================");
SimpleCircularArray simpleCircularArray = new SimpleCircularArray(10);
for (int i = 0; i < 10; i++)
simpleCircularArray.addLast(new Bucket(String.valueOf(i)));
for (Bucket bucket : simpleCircularArray)
System.out.println(bucket.getName());
simpleCircularArray.clear();
System.out.println("======================================");
for (int i = 0; i < 5; i++)
simpleCircularArray.addLast(new Bucket(String.valueOf(i)));
for (Bucket bucket : simpleCircularArray)
System.out.println(bucket.getName());
simpleCircularArray.clear();
System.out.println("======================================");
for (int i = 0; i < 3; i++)
simpleCircularArray.addLast(new Bucket(String.valueOf(i)));
for (Bucket bucket : simpleCircularArray)
System.out.println(bucket.getName());
simpleCircularArray.clear();
System.out.println("======================================");
for (int i = 0; i < 500; i++)
simpleCircularArray.addLast(new Bucket(String.valueOf(i)));
for (Bucket bucket : simpleCircularArray)
System.out.println(bucket.getName());
以上是关于java实现环形数组的主要内容,如果未能解决你的问题,请参考以下文章