blob: 2e34b1c41c85cf818fed7b0c4ea526be76c90a09 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.apache.jena.mem;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function ;
import org.apache.jena.shared.BrokenException ;
import org.apache.jena.util.iterator.ExtendedIterator;
import org.apache.jena.util.iterator.NiceIterator;
/**
An implementation of BunchMap that does open-addressed hashing.
*/
public class HashedBunchMap extends HashCommon<Object> implements BunchMap
{
protected TripleBunch [] values;
public HashedBunchMap()
{
super( 10 );
values = new TripleBunch[capacity];
}
@Override protected Object[] newKeyArray( int size )
{ return new Object[size]; }
/**
Clear this map: all entries are removed. The keys <i>and value</i> array
elements are set to null (so the values may be garbage-collected).
*/
@Override
public void clear()
{
size = 0;
for (int i = 0; i < capacity; i += 1) keys[i] = values[i] = null;
}
@Override
public long size()
{ return size; }
@Override
public TripleBunch get( Object key )
{
int slot = findSlot( key );
return slot < 0 ? values[~slot] : null;
}
@Override
public void put( Object key, TripleBunch value )
{
int slot = findSlot( key );
if (slot < 0)
values[~slot] = value;
else
put$(slot, key, value) ;
}
@Override
public TripleBunch getOrSet( Object key, Function<Object, TripleBunch> setter) {
int slot = findSlot( key );
if (slot < 0)
// Get.
return values[~slot] ;
// Or set value.
TripleBunch value = setter.apply(key) ;
put$(slot, key, value) ;
return value ;
}
private void put$(int slot, Object key, TripleBunch value) {
keys[slot] = key;
values[slot] = value;
size += 1;
if ( size == threshold )
grow();
}
protected void grow()
{
Object [] oldContents = keys;
TripleBunch [] oldValues = values;
final int oldCapacity = capacity;
growCapacityAndThreshold();
keys = newKeyArray( capacity );
values = new TripleBunch[capacity];
for (int i = 0; i < oldCapacity; i += 1)
{
Object key = oldContents[i];
if (key != null)
{
int j = findSlot( key );
if (j < 0)
{
throw new BrokenException( "oh dear, already have a slot for " + key + ", viz " + ~j );
}
keys[j] = key;
values[j] = oldValues[i];
}
}
}
/**
Called by HashCommon when a key is removed: remove
associated element of the <code>values</code> array.
*/
@Override protected void removeAssociatedValues( int here )
{ values[here] = null; }
/**
Called by HashCommon when a key is moved: move the
associated element of the <code>values</code> array.
*/
@Override protected void moveAssociatedValues( int here, int scan )
{ values[here] = values[scan]; }
@Override public Iterator<TripleBunch> iterator()
{
final List<Object> movedKeys = new ArrayList<>();
ExtendedIterator<TripleBunch> basic = new BasicValueIterator( changes, movedKeys );
ExtendedIterator<TripleBunch> leftovers = new MovedValuesIterator( changes, movedKeys );
return basic.andThen( leftovers );
}
/**
The MovedKeysIterator iterates over the elements of the <code>keys</code>
list. It's not sufficient to just use List::iterator, because the .remove
method must remove elements from the hash table itself.
<p>
Note that the list supplied on construction will be empty: it is filled before
the first call to <code>hasNext()</code>.
*/
protected final class MovedValuesIterator extends NiceIterator<TripleBunch>
{
private final List<Object> movedKeys;
protected int index = 0;
final int initialChanges;
protected MovedValuesIterator(int initialChanges, List<Object> movedKeys)
{
this.movedKeys = movedKeys;
this.initialChanges = initialChanges;
}
@Override public boolean hasNext()
{
return index < movedKeys.size();
}
@Override public TripleBunch next()
{
if (changes > initialChanges) throw new ConcurrentModificationException( "changes " + changes + " > initialChanges " + initialChanges );
if (index < movedKeys.size()) return get(movedKeys.get( index++ ));
return noElements( "" );
}
@Override public void forEachRemaining(Consumer<? super TripleBunch> action)
{
while(index < movedKeys.size()) action.accept( get(movedKeys.get( index++ )) );
if (changes > initialChanges) throw new ConcurrentModificationException();
}
@Override public void remove()
{
if (changes > initialChanges) throw new ConcurrentModificationException();
primitiveRemove( movedKeys.get( index - 1 ) );
}
}
/**
The BasicKeyIterator iterates over the <code>keys</code> array.
If a .remove call moves an unprocessed key underneath the iterator's
index, that key value is added to the <code>movedKeys</code>
list supplied to the constructor.
*/
protected final class BasicValueIterator extends NiceIterator<TripleBunch>
{
protected final List<Object> movedKeys;
int pos = capacity-1;
final int initialChanges;
protected BasicValueIterator(int initialChanges, List<Object> movedKeys)
{
this.movedKeys = movedKeys;
this.initialChanges = initialChanges;
}
@Override public boolean hasNext()
{
while(-1 < pos)
{
if(null != values[pos])
return true;
pos--;
}
return false;
}
@Override public TripleBunch next()
{
if (changes > initialChanges) throw new ConcurrentModificationException();
if (-1 < pos && null != values[pos]) return values[pos--];
throw new NoSuchElementException("HashCommon keys");
}
@Override public void forEachRemaining(Consumer<? super TripleBunch> action)
{
while(-1 < pos)
{
if(null != values[pos]) action.accept(values[pos]);
pos--;
}
if (changes > initialChanges) throw new ConcurrentModificationException();
}
@Override public void remove()
{
if (changes > initialChanges) throw new ConcurrentModificationException();
// System.err.println( ">> keyIterator::remove, size := " + size +
// ", removing " + keys[index + 1] );
Object moved = removeFrom( pos + 1 );
if (moved != null) movedKeys.add( moved );
if (size < 0) throw new BrokenException( "BROKEN" );
}
}
@Override public Spliterator<TripleBunch> spliterator() {
final var initialChanges = changes;
final Runnable checkForConcurrentModification = () ->
{
if (changes != initialChanges) throw new ConcurrentModificationException();
};
return new SparseArraySpliterator<>(values, size, checkForConcurrentModification);
}
}