-
Notifications
You must be signed in to change notification settings - Fork 19.9k
/
Copy pathORSet.java
191 lines (170 loc) · 5.6 KB
/
ORSet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package com.thealgorithms.datastructures.crdt;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* ORSet (Observed-Removed Set) is a state-based CRDT (Conflict-free Replicated Data Type)
* that supports both addition and removal of elements. This particular implementation follows
* the Add-Wins strategy, meaning that in case of conflicting add and remove operations,
* the add operation takes precedence. The merge operation of two OR-Sets ensures that
* elements added at any replica are eventually observed at all replicas. Removed elements,
* once observed, are never reintroduced.
* This OR-Set implementation provides methods for adding elements, removing elements,
* checking for element existence, retrieving the set of elements, comparing with other OR-Sets,
* and merging with another OR-Set to create a new OR-Set containing all unique elements
* from both sets.
*
* @author itakurah (Niklas Hoefflin) (https://github.com/itakurah)
* @see <a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type">Conflict-free_replicated_data_type</a>
* @see <a href="https://github.com/itakurah">itakurah (Niklas Hoefflin)</a>
*/
public class ORSet<T> {
private final Set<Pair<T>> elements;
private final Set<Pair<T>> tombstones;
/**
* Constructs an empty OR-Set.
*/
public ORSet() {
this.elements = new HashSet<>();
this.tombstones = new HashSet<>();
}
/**
* Checks if the set contains the specified element.
*
* @param element the element to check for
* @return true if the set contains the element, false otherwise
*/
public boolean contains(T element) {
return elements.stream().anyMatch(pair -> pair.getElement().equals(element));
}
/**
* Retrieves the elements in the set.
*
* @return a set containing the elements
*/
public Set<T> elements() {
Set<T> result = new HashSet<>();
elements.forEach(pair -> result.add(pair.getElement()));
return result;
}
/**
* Adds the specified element to the set.
*
* @param element the element to add
*/
public void add(T element) {
String n = prepare();
effect(element, n);
}
/**
* Removes the specified element from the set.
*
* @param element the element to remove
*/
public void remove(T element) {
Set<Pair<T>> pairsToRemove = prepare(element);
effect(pairsToRemove);
}
/**
* Collect all pairs with the specified element.
*
* @param element the element to collect pairs for
* @return a set of pairs with the specified element to be removed
*/
private Set<Pair<T>> prepare(T element) {
Set<Pair<T>> pairsToRemove = new HashSet<>();
for (Pair<T> pair : elements) {
if (pair.getElement().equals(element)) {
pairsToRemove.add(pair);
}
}
return pairsToRemove;
}
/**
* Generates a unique tag for the element.
*
* @return the unique tag
*/
private String prepare() {
return generateUniqueTag();
}
/**
* Adds the element with the specified unique tag to the set.
*
* @param element the element to add
* @param n the unique tag associated with the element
*/
private void effect(T element, String n) {
Pair<T> pair = new Pair<>(element, n);
elements.add(pair);
elements.removeAll(tombstones);
}
/**
* Removes the specified pairs from the set.
*
* @param pairsToRemove the pairs to remove
*/
private void effect(Set<Pair<T>> pairsToRemove) {
elements.removeAll(pairsToRemove);
tombstones.addAll(pairsToRemove);
}
/**
* Generates a unique tag.
*
* @return the unique tag
*/
private String generateUniqueTag() {
return UUID.randomUUID().toString();
}
/**
* Compares this Add-Wins OR-Set with another OR-Set to check if elements and tombstones are a subset.
*
* @param other the other OR-Set to compare
* @return true if the sets are subset, false otherwise
*/
public boolean compare(ORSet<T> other) {
Set<Pair<T>> union = new HashSet<>(elements);
union.addAll(tombstones);
Set<Pair<T>> otherUnion = new HashSet<>(other.elements);
otherUnion.addAll(other.tombstones);
return otherUnion.containsAll(union) && other.tombstones.containsAll(tombstones);
}
/**
* Merges this Add-Wins OR-Set with another OR-Set.
*
* @param other the other OR-Set to merge
*/
public void merge(ORSet<T> other) {
elements.removeAll(other.tombstones);
other.elements.removeAll(tombstones);
elements.addAll(other.elements);
tombstones.addAll(other.tombstones);
}
/**
* Represents a pair containing an element and a unique tag.
*
* @param <T> the type of the element in the pair
*/
public static class Pair<T> {
private final T element;
private final String uniqueTag;
/**
* Constructs a pair with the specified element and unique tag.
*
* @param element the element in the pair
* @param uniqueTag the unique tag associated with the element
*/
public Pair(T element, String uniqueTag) {
this.element = element;
this.uniqueTag = uniqueTag;
}
/**
* Gets the element from the pair.
*
* @return the element
*/
public T getElement() {
return element;
}
}
}