From ed22318874f6a185b17a35815014fb41cf79d8b7 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 27 Jan 2014 20:03:49 +0400 Subject: [PATCH] RedissonSortedSet added --- src/main/java/org/redisson/Redisson.java | 23 + .../java/org/redisson/RedissonSortedSet.java | 462 ++++++++++++++++++ .../org/redisson/RedissonSubSortedSet.java | 304 ++++++++++++ .../java/org/redisson/core/RSortedSet.java | 10 + .../org/redisson/RedissonSortedSetTest.java | 257 ++++++++++ src/test/java/org/redisson/TestObject.java | 13 +- 6 files changed, 1068 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/redisson/RedissonSortedSet.java create mode 100644 src/main/java/org/redisson/RedissonSubSortedSet.java create mode 100644 src/main/java/org/redisson/core/RSortedSet.java create mode 100644 src/test/java/org/redisson/RedissonSortedSetTest.java diff --git a/src/main/java/org/redisson/Redisson.java b/src/main/java/org/redisson/Redisson.java index 983977808..1e2575910 100644 --- a/src/main/java/org/redisson/Redisson.java +++ b/src/main/java/org/redisson/Redisson.java @@ -26,11 +26,14 @@ import org.redisson.core.RLock; import org.redisson.core.RMap; import org.redisson.core.RQueue; import org.redisson.core.RSet; +import org.redisson.core.RSortedSet; import org.redisson.core.RTopic; import org.redisson.misc.ReferenceMap; import org.redisson.misc.ReferenceMap.ReferenceType; import org.redisson.misc.ReferenceMap.RemoveValueListener; +import com.lambdaworks.redis.RedisConnection; + /** * Main infrastructure class allows to get access * to all Redisson objects on top of Redis server. @@ -58,6 +61,7 @@ public class Redisson { private final ConcurrentMap atomicLongsMap = new ReferenceMap(ReferenceType.STRONG, ReferenceType.SOFT); private final ConcurrentMap queuesMap = new ReferenceMap(ReferenceType.STRONG, ReferenceType.SOFT); private final ConcurrentMap setsMap = new ReferenceMap(ReferenceType.STRONG, ReferenceType.SOFT); + private final ConcurrentMap sortedSetMap = new ReferenceMap(ReferenceType.STRONG, ReferenceType.SOFT); private final ConcurrentMap listsMap = new ReferenceMap(ReferenceType.STRONG, ReferenceType.SOFT); private final ConcurrentMap mapsMap = new ReferenceMap(ReferenceType.STRONG, ReferenceType.SOFT); @@ -170,6 +174,25 @@ public class Redisson { return set; } + /** + * Returns distributed sorted set instance by name. + * + * @param name of the distributed set + * @return distributed set + */ + public RSortedSet getSortedSet(String name) { + RedissonSortedSet set = sortedSetMap.get(name); + if (set == null) { + set = new RedissonSortedSet(connectionManager, name); + RedissonSortedSet oldSet = sortedSetMap.putIfAbsent(name, set); + if (oldSet != null) { + set = oldSet; + } + } + + return set; + } + /** * Returns distributed topic instance by name. * diff --git a/src/main/java/org/redisson/RedissonSortedSet.java b/src/main/java/org/redisson/RedissonSortedSet.java new file mode 100644 index 000000000..4fc4e8f6f --- /dev/null +++ b/src/main/java/org/redisson/RedissonSortedSet.java @@ -0,0 +1,462 @@ +package org.redisson; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.SortedSet; + +import org.redisson.connection.ConnectionManager; +import org.redisson.core.RSortedSet; + +import com.lambdaworks.redis.RedisConnection; +import com.lambdaworks.redis.ScoredValue; + +/** + * + * @author Nikita Koksharov + * + * @param + */ +public class RedissonSortedSet extends RedissonObject implements RSortedSet { + + public static class BinarySearchResult { + + private V value; + private Double score; + private int index; + + public BinarySearchResult(V value, double score) { + super(); + this.value = value; + this.score = score; + } + + public BinarySearchResult() { + } + + public void setIndex(int index) { + this.index = index; + } + public int getIndex() { + return index; + } + + public V getValue() { + return value; + } + + public Double getScore() { + return score; + } + + } + + private final ConnectionManager connectionManager; + private final Comparator comparator = Collections.reverseOrder(Collections.reverseOrder()); + + RedissonSortedSet(ConnectionManager connectionManager, String name) { + super(name); + this.connectionManager = connectionManager; + } + + @Override + public int size() { + RedisConnection connection = connectionManager.connection(); + try { + return size(connection); + } finally { + connectionManager.release(connection); + } + } + + private int size(RedisConnection connection) { + return connection.zcard(getName()).intValue(); + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean contains(Object o) { + RedisConnection connection = connectionManager.connection(); + try { + return binarySearch((V)o, connection).getIndex() >= 0; + } finally { + connectionManager.release(connection); + } + } + + public Iterator iterator() { + double startScore; + RedisConnection connection = connectionManager.connection(); + try { + startScore = getScoreAtIndex(0, connection); + } finally { + connectionManager.release(connection); + } + + return iterator(startScore, Double.MAX_VALUE); + } + + public Iterator iterator(final double startScore, final double endScore) { + return new Iterator() { + + private double currentScore = startScore; + private boolean removeExecuted; + private V value; + + @Override + public boolean hasNext() { + RedisConnection connection = connectionManager.connection(); + try { + Long remains = connection.zcount(getName(), currentScore, endScore); + return remains > 0; + } finally { + connectionManager.release(connection); + } + } + + @Override + public V next() { + if (!hasNext()) { + throw new NoSuchElementException("No such element at index " + currentScore); + } + removeExecuted = false; + + RedisConnection connection = connectionManager.connection(); + try { + double lastScore = getScoreAtIndex(-1, connection); + double scoreDiff = lastScore - currentScore; + + Long remains = connection.zcount(getName(), currentScore, lastScore); + if (remains == 0) { + throw new NoSuchElementException("No more elements!"); + } + // TODO don't load whole set in memory +// if (remains < 50) { + List> values = connection.zrangebyscoreWithScores(getName(), currentScore, Double.MAX_VALUE); + if (values.isEmpty()) { + throw new NoSuchElementException("No more elements!"); + } + + ScoredValue val = values.get(0); + value = val.value; + + if (values.size() > 1) { + ScoredValue nextVal = values.get(1); + currentScore = nextVal.score; + } else { + currentScore = endScore; + } + return value; +// } + } finally { + connectionManager.release(connection); + } + } + + @Override + public void remove() { + if (removeExecuted) { + throw new IllegalStateException("Element been already deleted"); + } + + RedissonSortedSet.this.remove(value); + removeExecuted = true; + } + + }; + } + + @Override + public Object[] toArray() { + RedisConnection connection = connectionManager.connection(); + try { + return connection.zrange(getName(), 0, -1).toArray(); + } finally { + connectionManager.release(connection); + } + } + + @Override + public T[] toArray(T[] a) { + RedisConnection connection = connectionManager.connection(); + try { + return connection.zrange(getName(), 0, -1).toArray(a); + } finally { + connectionManager.release(connection); + } + } + + @Override + public boolean add(V value) { + RedisConnection connection = connectionManager.connection(); + try { + while (true) { + connection.watch(getName()); + + BinarySearchResult res = binarySearch(value, connection); + if (res.getIndex() < 0) { + double score = calcNewScore(res.getIndex(), connection); + + connection.multi(); + connection.zadd(getName(), score, value); + List re = connection.exec(); + if (re.size() == 1) { + Object val = re.iterator().next(); + return val != null && ((Number)val).intValue() > 0; + } + } else { + connection.unwatch(); + return false; + } + } + } finally { + connectionManager.release(connection); + } + } + + /** + * score for entry added before head = head / 2 + * score for entry added after tail = tail + 1000000 + * score for entry inserted between head and tail = head + (tail - head) / 2 + * + * @param index + * @param connection + * @return score for index + */ + public double calcNewScore(int index, RedisConnection connection) { + if (index >= 0) { + throw new IllegalStateException("index should be negative, but value is " + index); + } + index = -(index + 1); + + double score = getScoreAtIndex(index, connection); + if (index == 0) { + if (score < 0) { + score = 1000000; + } else { + score /= 2; + } + } else { + double beginScore = getScoreAtIndex(index-1, connection); + if (score < 0) { + score = beginScore + 1000000; + } else { + score = beginScore + (score - beginScore) / 2; + } + } + return score; + } + + @Override + public boolean remove(Object value) { + RedisConnection connection = connectionManager.connection(); + try { + BinarySearchResult res = binarySearch((V) value, connection); + if (res.getIndex() < 0) { + return false; + } + return connection.zremrangebyscore(getName(), res.getScore(), res.getScore()) > 0; + } finally { + connectionManager.release(connection); + } + } + + @Override + public boolean containsAll(Collection c) { + for (Object object : c) { + if (!contains(object)) { + return false; + } + } + return true; + } + + @Override + public boolean addAll(Collection c) { + boolean changed = false; + for (V v : c) { + if (add(v)) { + changed = true; + } + } + return changed; + } + + @Override + public boolean retainAll(Collection c) { + boolean changed = false; + for (Object object : this) { + if (!c.contains(object)) { + remove(object); + changed = true; + } + } + return changed; + } + + @Override + public boolean removeAll(Collection c) { + boolean changed = false; + for (Object obj : c) { + if (remove(obj)) { + changed = true; + } + } + return changed; + } + + @Override + public void clear() { + RedisConnection connection = connectionManager.connection(); + try { + connection.del(getName()); + } finally { + connectionManager.release(connection); + } + } + + @Override + public Comparator comparator() { + // TODO Auto-generated method stub + return null; + } + + @Override + public SortedSet subSet(V fromElement, V toElement) { + return new RedissonSubSortedSet(this, connectionManager, fromElement, toElement); + } + + @Override + public SortedSet headSet(V toElement) { + return subSet(null, toElement); + } + + @Override + public SortedSet tailSet(V fromElement) { + return subSet(fromElement, null); + } + + @Override + public V first() { + RedisConnection connection = connectionManager.connection(); + try { + BinarySearchResult res = getAtIndex(0, connection); + if (res.getScore() == null) { + throw new NoSuchElementException(); + } + return res.getValue(); + } finally { + connectionManager.release(connection); + } + } + + @Override + public V last() { + RedisConnection connection = connectionManager.connection(); + try { + BinarySearchResult res = getAtIndex(-1, connection); + if (res.getScore() == null) { + throw new NoSuchElementException(); + } + return res.getValue(); + } finally { + connectionManager.release(connection); + } + } + + @Override + public boolean trySetComparator(Comparator comparator) { + // TODO Auto-generated method stub + return false; + } + + private double getScoreAtIndex(int index, RedisConnection connection) { + List> res = connection.zrangeWithScores(getName(), index, index); + if (res.isEmpty()) { + return -1; + } + return res.get(0).score; + } + + private BinarySearchResult getAtIndex(int index, RedisConnection connection) { + List> res = connection.zrangeWithScores(getName(), index, index); + if (res.isEmpty()) { + return new BinarySearchResult(); + } + return new BinarySearchResult(res.get(0).value, res.get(0).score); + } + + /** + * Binary search algorithm + * + * @param value + * @param connection + * @param lowerIndex + * @param upperIndex + * @return + */ + public BinarySearchResult binarySearch(V value, RedisConnection connection, int lowerIndex, int upperIndex) { + while (lowerIndex <= upperIndex) { + int index = lowerIndex + (upperIndex - lowerIndex) / 2; + + BinarySearchResult indexRes = getAtIndex(index, connection); + int cmp = comparator.compare(value, indexRes.getValue()); + + if (cmp == 0) { + indexRes.setIndex(index); + return indexRes; + } else if (cmp < 0) { + upperIndex = index - 1; + } else { + lowerIndex = index + 1; + } + } + + BinarySearchResult indexRes = new BinarySearchResult(); + indexRes.setIndex(-(lowerIndex + 1)); + return indexRes; + } + + public BinarySearchResult binarySearch(V value, RedisConnection connection) { + int upperIndex = size(connection) - 1; + return binarySearch(value, connection, 0, upperIndex); + } + + public double score(V value, RedisConnection connection, int indexDiff, boolean tail) { + BinarySearchResult res = binarySearch(value, connection); + if (res.getIndex() < 0) { + BinarySearchResult element = getAtIndex(-res.getIndex() + indexDiff, connection); + return element.getScore(); + } + int ind = res.getIndex(); + if (tail) { + ind = res.getIndex() - indexDiff; + } + BinarySearchResult element = getAtIndex(ind, connection); + return element.getScore(); + } + + public String toString() { + Iterator it = iterator(); + if (! it.hasNext()) + return "[]"; + + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (;;) { + V e = it.next(); + sb.append(e == this ? "(this Collection)" : e); + if (! it.hasNext()) + return sb.append(']').toString(); + sb.append(',').append(' '); + } + } + +} diff --git a/src/main/java/org/redisson/RedissonSubSortedSet.java b/src/main/java/org/redisson/RedissonSubSortedSet.java new file mode 100644 index 000000000..efa629c48 --- /dev/null +++ b/src/main/java/org/redisson/RedissonSubSortedSet.java @@ -0,0 +1,304 @@ +package org.redisson; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.SortedSet; + +import org.redisson.RedissonSortedSet.BinarySearchResult; +import org.redisson.connection.ConnectionManager; + +import com.lambdaworks.redis.RedisConnection; + +/** + * + * @author Nikita Koksharov + * + * @param + */ +class RedissonSubSortedSet implements SortedSet { + + private ConnectionManager connectionManager; + private RedissonSortedSet redissonSortedSet; + + private V headValue; + private V tailValue; + + public RedissonSubSortedSet(RedissonSortedSet redissonSortedSet, ConnectionManager connectionManager, V headValue, V tailValue) { + super(); + this.headValue = headValue; + this.tailValue = tailValue; + + this.connectionManager = connectionManager; + this.redissonSortedSet = redissonSortedSet; + } + + @Override + public int size() { + RedisConnection connection = connectionManager.connection(); + try { + double headScore = getHeadScore(connection); + double tailScore = getTailScore(connection); + + return connection.zcount(redissonSortedSet.getName(), headScore, tailScore).intValue(); + } finally { + connectionManager.release(connection); + } + } + + private double getTailScore(RedisConnection connection) { + if (tailValue != null) { + return redissonSortedSet.score(tailValue, connection, 0, true); + } + return Double.MAX_VALUE; + } + + private double getHeadScore(RedisConnection connection) { + if (headValue != null) { + return redissonSortedSet.score(headValue, connection, -1, false); + } + return 0; + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean contains(Object o) { + RedisConnection connection = connectionManager.connection(); + try { + double headScore = getHeadScore(connection); + double tailScore = getTailScore(connection); + + BinarySearchResult res = redissonSortedSet.binarySearch((V)o, connection); + return res.getScore() < tailScore && res.getScore() > headScore; + } finally { + connectionManager.release(connection); + } + } + + @Override + public Iterator iterator() { + RedisConnection connection = connectionManager.connection(); + try { + double headScore = getHeadScore(connection); + double tailScore = getTailScore(connection); + return redissonSortedSet.iterator(headScore, tailScore); + } finally { + connectionManager.release(connection); + } + } + + @Override + public Object[] toArray() { + RedisConnection connection = connectionManager.connection(); + try { + double headScore = getHeadScore(connection); + double tailScore = getTailScore(connection); + return connection.zrangebyscore(redissonSortedSet.getName(), headScore, tailScore).toArray(); + } finally { + connectionManager.release(connection); + } + } + + @Override + public T[] toArray(T[] a) { + RedisConnection connection = connectionManager.connection(); + try { + double headScore = getHeadScore(connection); + double tailScore = getTailScore(connection); + return connection.zrangebyscore(redissonSortedSet.getName(), headScore, tailScore).toArray(a); + } finally { + connectionManager.release(connection); + } + } + + @Override + public boolean add(V e) { + RedisConnection connection = connectionManager.connection(); + try { + double headScore = getHeadScore(connection); + double tailScore = getTailScore(connection); + + BinarySearchResult res = redissonSortedSet.binarySearch(e, connection); + if (res.getScore() == null) { + double score = redissonSortedSet.calcNewScore(res.getIndex(), connection); + if (score < tailScore && score > headScore) { + return redissonSortedSet.add(e); + } else { + throw new IllegalArgumentException("value out of range"); + } + } + return false; + } finally { + connectionManager.release(connection); + } + } + + @Override + public boolean remove(Object o) { + RedisConnection connection = connectionManager.connection(); + try { + double headScore = getHeadScore(connection); + double tailScore = getTailScore(connection); + + BinarySearchResult res = redissonSortedSet.binarySearch((V)o, connection); + if (res.getScore() != null && res.getScore() < tailScore && res.getScore() > headScore) { + return redissonSortedSet.remove(o); + } + return false; + } finally { + connectionManager.release(connection); + } + } + + @Override + public boolean containsAll(Collection c) { + for (Object object : c) { + if (!contains(object)) { + return false; + } + } + return true; + } + + @Override + public boolean addAll(Collection c) { + boolean changed = false; + for (V v : c) { + if (add(v)) { + changed = true; + } + } + return changed; + } + + @Override + public boolean retainAll(Collection c) { + boolean changed = false; + for (Object object : this) { + if (!c.contains(object)) { + remove(object); + changed = true; + } + } + return changed; + } + + @Override + public boolean removeAll(Collection c) { + boolean changed = false; + for (Object obj : c) { + if (remove(obj)) { + changed = true; + } + } + return changed; + } + + @Override + public void clear() { + RedisConnection connection = connectionManager.connection(); + try { + double headScore = getHeadScore(connection); + double tailScore = getTailScore(connection); + connection.zremrangebyscore(redissonSortedSet.getName(), headScore, tailScore); + } finally { + connectionManager.release(connection); + } + } + + @Override + public Comparator comparator() { + return redissonSortedSet.comparator(); + } + + @Override + public SortedSet subSet(V fromElement, V toElement) { + // TODO check bounds + if (fromElement == null) { + fromElement = headValue; + } + if (toElement == null) { + toElement = tailValue; + } + return new RedissonSubSortedSet(redissonSortedSet, connectionManager, fromElement, toElement); + } + + @Override + public SortedSet headSet(V toElement) { + return subSet(null, toElement); + } + + @Override + public SortedSet tailSet(V fromElement) { + return subSet(fromElement, null); + } + + @Override + public V first() { + RedisConnection connection = connectionManager.connection(); + try { + if (headValue != null) { + BinarySearchResult res = redissonSortedSet.binarySearch(headValue, connection); + if (res.getIndex() < 0) { + double headScore = redissonSortedSet.calcNewScore(res.getIndex(), connection); + double tailScore = getTailScore(connection); + List vals = connection.zrangebyscore(redissonSortedSet.getName(), headScore, tailScore); + if (vals.isEmpty()) { + throw new NoSuchElementException(); + } + return vals.get(0); + } + return res.getValue(); + } + return redissonSortedSet.first(); + } finally { + connectionManager.release(connection); + } + } + + @Override + public V last() { + RedisConnection connection = connectionManager.connection(); + try { + if (tailValue != null) { + BinarySearchResult res = redissonSortedSet.binarySearch(tailValue, connection); + if (res.getIndex() < 0) { + double tailScore = redissonSortedSet.calcNewScore(res.getIndex(), connection); + double headScore = getHeadScore(connection); + List vals = connection.zrangebyscore(redissonSortedSet.getName(), headScore, tailScore); + if (vals.isEmpty()) { + throw new NoSuchElementException(); + } + return vals.get(0); + } + return res.getValue(); + } + return redissonSortedSet.last(); + } finally { + connectionManager.release(connection); + } + } + + public String toString() { + Iterator it = iterator(); + if (! it.hasNext()) + return "[]"; + + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (;;) { + V e = it.next(); + sb.append(e == this ? "(this Collection)" : e); + if (! it.hasNext()) + return sb.append(']').toString(); + sb.append(',').append(' '); + } + } + +} diff --git a/src/main/java/org/redisson/core/RSortedSet.java b/src/main/java/org/redisson/core/RSortedSet.java new file mode 100644 index 000000000..5ecb35b87 --- /dev/null +++ b/src/main/java/org/redisson/core/RSortedSet.java @@ -0,0 +1,10 @@ +package org.redisson.core; + +import java.util.Comparator; +import java.util.SortedSet; + +public interface RSortedSet extends SortedSet, RObject { + + boolean trySetComparator(Comparator comparator); + +} diff --git a/src/test/java/org/redisson/RedissonSortedSetTest.java b/src/test/java/org/redisson/RedissonSortedSetTest.java new file mode 100644 index 000000000..e29a7b309 --- /dev/null +++ b/src/test/java/org/redisson/RedissonSortedSetTest.java @@ -0,0 +1,257 @@ +package org.redisson; + +import java.util.Arrays; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.redisson.core.RSortedSet; + +public class RedissonSortedSetTest extends BaseTest { + + Redisson redisson; + + @Before + public void before() { + redisson = Redisson.create(); + } + + @After + public void after() { + redisson.flushdb(); + redisson.shutdown(); + } + + @Test + public void testOrder2() { + TreeSet set = new TreeSet(); + + set.add(1); + set.add(2); + set.add(3); + set.add(4); + set.add(5); + + SortedSet hs = set.headSet(6); + } + +// @Test(expected = IllegalArgumentException.class) + public void testTailSet() { + RSortedSet set = redisson.getSortedSet("set"); + + set.add(1); + set.add(2); + set.add(3); + set.add(4); + set.add(5); + + SortedSet hs = set.tailSet(3); + hs.add(10); + + MatcherAssert.assertThat(hs, Matchers.contains(3, 4, 5, 10)); + + set.remove(4); + + MatcherAssert.assertThat(hs, Matchers.contains(3, 5, 10)); + + set.remove(3); + + MatcherAssert.assertThat(hs, Matchers.contains(5, 10)); + + hs.add(-1); + } + + +// @Test(expected = IllegalArgumentException.class) + public void testHeadSet() { + RSortedSet set = redisson.getSortedSet("set"); + + set.add(1); + set.add(2); + set.add(3); + set.add(4); + set.add(5); + + SortedSet hs = set.headSet(3); + hs.add(0); + + MatcherAssert.assertThat(hs, Matchers.contains(0, 1, 2)); + + set.remove(2); + + MatcherAssert.assertThat(hs, Matchers.contains(0, 1)); + + set.remove(3); + + hs.add(7); + } + + @Test(expected = IllegalArgumentException.class) + public void testTailSetTreeSet() { + TreeSet set = new TreeSet(); + + set.add(1); + set.add(2); + set.add(3); + set.add(4); + set.add(5); + + SortedSet hs = set.tailSet(3); + hs.add(10); + + MatcherAssert.assertThat(hs, Matchers.contains(3, 4, 5, 10)); + + set.remove(4); + + MatcherAssert.assertThat(hs, Matchers.contains(3, 5, 10)); + + set.remove(3); + + MatcherAssert.assertThat(hs, Matchers.contains(5, 10)); + + hs.add(-1); + } + + @Test(expected = IllegalArgumentException.class) + public void testHeadSetTreeSet() { + TreeSet set = new TreeSet(); + + set.add(1); + set.add(2); + set.add(3); + set.add(4); + set.add(5); + + SortedSet hs = set.headSet(3); + hs.add(0); + + MatcherAssert.assertThat(hs, Matchers.contains(0, 1, 2)); + + set.remove(2); + + MatcherAssert.assertThat(hs, Matchers.contains(0, 1)); + + set.remove(3); + + hs.add(7); + } + + @Test + public void testSort() { + RSortedSet set = redisson.getSortedSet("set"); + Assert.assertTrue(set.add(2)); + Assert.assertTrue(set.add(3)); + Assert.assertTrue(set.add(1)); + Assert.assertTrue(set.add(4)); + Assert.assertTrue(set.add(10)); + Assert.assertTrue(set.add(-1)); + Assert.assertTrue(set.add(0)); + + MatcherAssert.assertThat(set, Matchers.contains(-1, 0, 1, 2, 3, 4, 10)); + + Assert.assertEquals(-1, (int)set.first()); + Assert.assertEquals(10, (int)set.last()); + } + + @Test + public void testRemove() { + RSortedSet set = redisson.getSortedSet("set"); + set.add(5); + set.add(3); + set.add(1); + set.add(2); + set.add(4); + + Assert.assertFalse(set.remove(0)); + Assert.assertTrue(set.remove(3)); + + Assert.assertThat(set, Matchers.contains(1, 2, 4, 5)); + } + + @Test + public void testRetainAll() { + Set set = redisson.getSortedSet("set"); + for (int i = 0; i < 200; i++) { + set.add(i); + } + + Assert.assertTrue(set.retainAll(Arrays.asList(1, 2))); + Assert.assertEquals(2, set.size()); + } + + @Test + public void testContainsAll() { + Set set = redisson.getSortedSet("set"); + for (int i = 0; i < 200; i++) { + set.add(i); + } + + Assert.assertTrue(set.containsAll(Arrays.asList(30, 11))); + Assert.assertFalse(set.containsAll(Arrays.asList(30, 711, 11))); + } + + @Test + public void testToArray() { + Set set = redisson.getSortedSet("set"); + set.add("1"); + set.add("4"); + set.add("2"); + set.add("5"); + set.add("3"); + + MatcherAssert.assertThat(Arrays.asList(set.toArray()), Matchers.containsInAnyOrder("1", "2", "4", "5", "3")); + + String[] strs = set.toArray(new String[0]); + MatcherAssert.assertThat(Arrays.asList(strs), Matchers.containsInAnyOrder("1", "4", "2", "5", "3")); + } + + @Test + public void testContains() { + Set set = redisson.getSortedSet("set"); + + set.add(new TestObject("1", "2")); + set.add(new TestObject("1", "2")); + set.add(new TestObject("2", "3")); + set.add(new TestObject("3", "4")); + set.add(new TestObject("5", "6")); + + Assert.assertTrue(set.contains(new TestObject("2", "3"))); + Assert.assertTrue(set.contains(new TestObject("1", "2"))); + Assert.assertFalse(set.contains(new TestObject("1", "9"))); + } + + @Test + public void testDuplicates() { + Set set = redisson.getSortedSet("set"); + + set.add(new TestObject("1", "2")); + set.add(new TestObject("1", "2")); + set.add(new TestObject("2", "3")); + set.add(new TestObject("3", "4")); + set.add(new TestObject("5", "6")); + + Assert.assertEquals(4, set.size()); + } + + @Test + public void testSize() { + Set set = redisson.getSortedSet("set"); + set.add(1); + set.add(2); + set.add(3); + set.add(3); + set.add(4); + set.add(5); + set.add(5); + + Assert.assertEquals(5, set.size()); + } + + +} diff --git a/src/test/java/org/redisson/TestObject.java b/src/test/java/org/redisson/TestObject.java index 627f1a62e..a062c5d33 100644 --- a/src/test/java/org/redisson/TestObject.java +++ b/src/test/java/org/redisson/TestObject.java @@ -1,6 +1,6 @@ package org.redisson; -public class TestObject { +public class TestObject implements Comparable { private String name; private String value; @@ -22,4 +22,15 @@ public class TestObject { return value; } + @Override + public int compareTo(TestObject o) { + int res = name.compareTo(o.name); + if (res == 0) { + return value.compareTo(o.value); + } + return res; + } + + + }