Jad*_*mas 17 java collections mutable set generic-collections
在Java中,集合仅在插入时仅检查对象与已在集合中的对象的相等性.这意味着如果在对象已经存在于集合中之后,它变得等于集合中的另一个对象,则该集合将保持两个相等的对象而不会抱怨.
编辑:例如,考虑一个简单的对象,并假设hashCode和equals是按照最佳实践/定义的
class Foo {
int foo;
Foo(int a){ foo = a; }
//+ equals and hashcode based on "foo"
}
Foo foo1 = new Foo(1);
Foo foo2 = new Foo(2);
Set<Foo> set = new HashSet<Foo>();
set.add(foo1);
set.add(foo2);
//Here the set has two unequal elements.
foo2.foo = 1;
//At this point, foo2 is equal to foo1, but is still in the set
//together with foo1.
Run Code Online (Sandbox Code Playgroud)
如何为可变对象设计一个集合类?我期望的行为如下:如果集合中的一个对象在任何时候变得等于集合中的另一个对象,则该集合中的该对象将被删除.有没有?是否有一种编程语言可以使这更容易实现?
我不认为这在一般意义上可以在Java中可靠地完成.没有一般机制来确保对象的变异采取某种行动.
有几种解决方案可能足以满足您的使用需求.
1.观察变化的要素
您可以尝试强制执行类似构造的观察者,其中您的Set类被注册为其所有项的Observer.这意味着您需要控制可以放入Set的对象类型(仅限Observable对象).此外,您需要确保Observables通知观察者每个可能影响hashcode和equals的更改.我不知道这样的任何类已经存在.就像下面的Ray提到的那样,你也需要注意潜在的并发问题.例:
package collectiontests.observer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
public class ChangeDetectingSet<E extends Observable> implements Set<E>, Observer {
private HashSet<E> innerSet;
public void update(Observable o, Object arg) {
innerSet.remove(o);
innerSet.add((E)o);
}
public int size() {
return innerSet.size();
}
public boolean isEmpty() {
return innerSet.isEmpty();
}
public boolean contains(Object o) {
return innerSet.contains(o);
}
public Iterator<E> iterator() {
return innerSet.iterator();
}
public Object[] toArray() {
return innerSet.toArray();
}
public <T> T[] toArray(T[] a) {
return innerSet.toArray(a);
}
public boolean add(E e) {
e.addObserver(this);
return innerSet.add(e);
}
public boolean remove(Object o) {
if(o instanceof Observable){
((Observable) o).deleteObserver(this);
}
return innerSet.remove(o);
}
public boolean containsAll(Collection<?> c) {
return innerSet.containsAll(c);
}
public boolean addAll(Collection<? extends E> c) {
boolean result = false;
for(E el: c){
result = result || add(el);
}
return result;
}
public boolean retainAll(Collection<?> c) {
Iterator<E> it = innerSet.iterator();
E el;
Collection<E> elementsToRemove = new ArrayList<E>();
while(it.hasNext()){
el = it.next();
if(!c.contains(el)){
elementsToRemove.add(el); //No changing the set while the iterator is going. Iterator.remove may not do what we want.
}
}
for(E e: elementsToRemove){
remove(e);
}
return !elementsToRemove.isEmpty(); //If it's empty there is no change and we should return false
}
public boolean removeAll(Collection<?> c) {
boolean result = false;
for(Object e: c){
result = result || remove(e);
}
return result;
}
public void clear() {
Iterator<E> it = innerSet.iterator();
E el;
while(it.hasNext()){
el = it.next();
el.deleteObserver(this);
}
innerSet.clear();
}
}
Run Code Online (Sandbox Code Playgroud)
每次可变对象发生变化时,都会导致性能下降.
2.使用Set时检查更改
如果您的集合中的对象经常更改,但很少使用集合本身,您可以尝试下面的Joe解决方案.他建议每当你调用一个方法时检查Set是否仍然正确.作为奖励,他的方法将适用于任何一组对象(不必将其限制为可观察对象).在性能方面,他的方法对于大型集合或经常使用的集合会有问题(因为需要在每次方法调用时检查整个集合).
可能实现Joe的方法:
package collectiontests.check;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class ListBasedSet<E> {
private List<E> innerList;
public ListBasedSet(){
this(null);
}
public ListBasedSet(Collection<E> elements){
if (elements != null){
innerList = new ArrayList<E>(elements);
} else {
innerList = new ArrayList<E>();
}
}
public void add(E e){
innerList.add(e);
}
public int size(){
return toSet().size();
}
public Iterator<E> iterator(){
return toSet().iterator();
}
public void remove(E e){
while(innerList.remove(e)); //Keep removing until they are all gone (so set behavior is kept)
}
public boolean contains(E e){
//I think you could just do innerList.contains here as it shouldn't care about duplicates
return innerList.contains(e);
}
private Set<E> toSet(){
return new HashSet<E>(innerList);
}
}
Run Code Online (Sandbox Code Playgroud)
另一种check always方法的实现(这个方法基于现有的set).如果要尽可能多地重用现有集合,这是可行的方法.
package collectiontests.check;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NavigableSet;
import java.util.SortedSet;
import java.util.TreeSet;
public class ChangeDetectingSet<E> extends TreeSet<E> {
private boolean compacting = false;
@SuppressWarnings("unchecked")
private void compact(){
//To avoid infinite loops, make sure we are not already compacting (compact also gets called in the methods used here)
if(!compacting){ //Warning: this is not thread-safe
compacting = true;
Object[] elements = toArray();
clear();
for(Object element: elements){
add((E)element); //Yes unsafe cast, but we're rather sure
}
compacting = false;
}
}
@Override
public boolean add(E e) {
compact();
return super.add(e);
}
@Override
public Iterator<E> iterator() {
compact();
return super.iterator();
}
@Override
public Iterator<E> descendingIterator() {
compact();
return super.descendingIterator();
}
@Override
public NavigableSet<E> descendingSet() {
compact();
return super.descendingSet();
}
@Override
public int size() {
compact();
return super.size();
}
@Override
public boolean isEmpty() {
compact();
return super.isEmpty();
}
@Override
public boolean contains(Object o) {
compact();
return super.contains(o);
}
@Override
public boolean remove(Object o) {
compact();
return super.remove(o);
}
@Override
public void clear() {
compact();
super.clear();
}
@Override
public boolean addAll(Collection<? extends E> c) {
compact();
return super.addAll(c);
}
@Override
public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
compact();
return super.subSet(fromElement, fromInclusive, toElement, toInclusive);
}
@Override
public NavigableSet<E> headSet(E toElement, boolean inclusive) {
compact();
return super.headSet(toElement, inclusive);
}
@Override
public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
compact();
return super.tailSet(fromElement, inclusive);
}
@Override
public SortedSet<E> subSet(E fromElement, E toElement) {
compact();
return super.subSet(fromElement, toElement);
}
@Override
public SortedSet<E> headSet(E toElement) {
compact();
return super.headSet(toElement);
}
@Override
public SortedSet<E> tailSet(E fromElement) {
compact();
return super.tailSet(fromElement);
}
@Override
public Comparator<? super E> comparator() {
compact();
return super.comparator();
}
@Override
public E first() {
compact();
return super.first();
}
@Override
public E last() {
compact();
return super.last();
}
@Override
public E lower(E e) {
compact();
return super.lower(e);
}
@Override
public E floor(E e) {
compact();
return super.floor(e);
}
@Override
public E ceiling(E e) {
compact();
return super.ceiling(e);
}
@Override
public E higher(E e) {
compact();
return super.higher(e);
}
@Override
public E pollFirst() {
compact();
return super.pollFirst();
}
@Override
public E pollLast() {
compact();
return super.pollLast();
}
@Override
public boolean removeAll(Collection<?> c) {
compact();
return super.removeAll(c);
}
@Override
public Object[] toArray() {
compact();
return super.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
compact();
return super.toArray(a);
}
@Override
public boolean containsAll(Collection<?> c) {
compact();
return super.containsAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
compact();
return super.retainAll(c);
}
@Override
public String toString() {
compact();
return super.toString();
}
}
Run Code Online (Sandbox Code Playgroud)
3.使用Scala集
你可以欺骗并消除可变对象(在某种意义上,你可以在你的集合中创建一个更改了一个属性的新对象).您可以查看Scala中的集合(我认为可以从Java调用Scala,但我不是100%肯定):http://www.scala-lang.org/api/current/scala/collection/不可改变/ IndexedSeq.html
| 归档时间: |
|
| 查看次数: |
5269 次 |
| 最近记录: |