Issue Fix #1779
This commit is contained in:
parent
100f8b8631
commit
51c14f1a9c
9 changed files with 6655 additions and 19 deletions
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.googlecode.concurrentlinkedhashmap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that can determine the weight of an entry. The total weight threshold
|
||||||
|
* is used to determine when an eviction is required.
|
||||||
|
*
|
||||||
|
* @author ben.manes@gmail.com (Ben Manes)
|
||||||
|
* @see <a href="http://code.google.com/p/concurrentlinkedhashmap/">
|
||||||
|
* http://code.google.com/p/concurrentlinkedhashmap/</a>
|
||||||
|
*/
|
||||||
|
public interface EntryWeigher<K, V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Measures an entry's weight to determine how many units of capacity that
|
||||||
|
* the key and value consumes. An entry must consume a minimum of one unit.
|
||||||
|
*
|
||||||
|
* @param key the key to weigh
|
||||||
|
* @param value the value to weigh
|
||||||
|
* @return the entry's weight
|
||||||
|
*/
|
||||||
|
int weightOf(K key, V value);
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.googlecode.concurrentlinkedhashmap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener registered for notification when an entry is evicted. An instance
|
||||||
|
* may be called concurrently by multiple threads to process entries. An
|
||||||
|
* implementation should avoid performing blocking calls or synchronizing on
|
||||||
|
* shared resources.
|
||||||
|
* <p>
|
||||||
|
* The listener is invoked by {@link ConcurrentLinkedHashMap} on a caller's
|
||||||
|
* thread and will not block other threads from operating on the map. An
|
||||||
|
* implementation should be aware that the caller's thread will not expect
|
||||||
|
* long execution times or failures as a side effect of the listener being
|
||||||
|
* notified. Execution safety and a fast turn around time can be achieved by
|
||||||
|
* performing the operation asynchronously, such as by submitting a task to an
|
||||||
|
* {@link java.util.concurrent.ExecutorService}.
|
||||||
|
*
|
||||||
|
* @author ben.manes@gmail.com (Ben Manes)
|
||||||
|
* @see <a href="http://code.google.com/p/concurrentlinkedhashmap/">
|
||||||
|
* http://code.google.com/p/concurrentlinkedhashmap/</a>
|
||||||
|
*/
|
||||||
|
public interface EvictionListener<K, V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call-back notification that the entry was evicted.
|
||||||
|
*
|
||||||
|
* @param key the entry's key
|
||||||
|
* @param value the entry's value
|
||||||
|
*/
|
||||||
|
void onEviction(K key, V value);
|
||||||
|
}
|
|
@ -0,0 +1,460 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.googlecode.concurrentlinkedhashmap;
|
||||||
|
|
||||||
|
import java.util.AbstractCollection;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linked list implementation of the {@link Deque} interface where the link
|
||||||
|
* pointers are tightly integrated with the element. Linked deques have no
|
||||||
|
* capacity restrictions; they grow as necessary to support usage. They are not
|
||||||
|
* thread-safe; in the absence of external synchronization, they do not support
|
||||||
|
* concurrent access by multiple threads. Null elements are prohibited.
|
||||||
|
* <p>
|
||||||
|
* Most <tt>LinkedDeque</tt> operations run in constant time by assuming that
|
||||||
|
* the {@link Linked} parameter is associated with the deque instance. Any usage
|
||||||
|
* that violates this assumption will result in non-deterministic behavior.
|
||||||
|
* <p>
|
||||||
|
* The iterators returned by this class are <em>not</em> <i>fail-fast</i>: If
|
||||||
|
* the deque is modified at any time after the iterator is created, the iterator
|
||||||
|
* will be in an unknown state. Thus, in the face of concurrent modification,
|
||||||
|
* the iterator risks arbitrary, non-deterministic behavior at an undetermined
|
||||||
|
* time in the future.
|
||||||
|
*
|
||||||
|
* @author ben.manes@gmail.com (Ben Manes)
|
||||||
|
* @param <E> the type of elements held in this collection
|
||||||
|
* @see <a href="http://code.google.com/p/concurrentlinkedhashmap/">
|
||||||
|
* http://code.google.com/p/concurrentlinkedhashmap/</a>
|
||||||
|
*/
|
||||||
|
final class LinkedDeque<E extends Linked<E>> extends AbstractCollection<E> implements Deque<E> {
|
||||||
|
|
||||||
|
// This class provides a doubly-linked list that is optimized for the virtual
|
||||||
|
// machine. The first and last elements are manipulated instead of a slightly
|
||||||
|
// more convenient sentinel element to avoid the insertion of null checks with
|
||||||
|
// NullPointerException throws in the byte code. The links to a removed
|
||||||
|
// element are cleared to help a generational garbage collector if the
|
||||||
|
// discarded elements inhabit more than one generation.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointer to first node.
|
||||||
|
* Invariant: (first == null && last == null) ||
|
||||||
|
* (first.prev == null)
|
||||||
|
*/
|
||||||
|
E first;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointer to last node.
|
||||||
|
* Invariant: (first == null && last == null) ||
|
||||||
|
* (last.next == null)
|
||||||
|
*/
|
||||||
|
E last;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Links the element to the front of the deque so that it becomes the first
|
||||||
|
* element.
|
||||||
|
*
|
||||||
|
* @param e the unlinked element
|
||||||
|
*/
|
||||||
|
void linkFirst(final E e) {
|
||||||
|
final E f = first;
|
||||||
|
first = e;
|
||||||
|
|
||||||
|
if (f == null) {
|
||||||
|
last = e;
|
||||||
|
} else {
|
||||||
|
f.setPrevious(e);
|
||||||
|
e.setNext(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Links the element to the back of the deque so that it becomes the last
|
||||||
|
* element.
|
||||||
|
*
|
||||||
|
* @param e the unlinked element
|
||||||
|
*/
|
||||||
|
void linkLast(final E e) {
|
||||||
|
final E l = last;
|
||||||
|
last = e;
|
||||||
|
|
||||||
|
if (l == null) {
|
||||||
|
first = e;
|
||||||
|
} else {
|
||||||
|
l.setNext(e);
|
||||||
|
e.setPrevious(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Unlinks the non-null first element. */
|
||||||
|
E unlinkFirst() {
|
||||||
|
final E f = first;
|
||||||
|
final E next = f.getNext();
|
||||||
|
f.setNext(null);
|
||||||
|
|
||||||
|
first = next;
|
||||||
|
if (next == null) {
|
||||||
|
last = null;
|
||||||
|
} else {
|
||||||
|
next.setPrevious(null);
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Unlinks the non-null last element. */
|
||||||
|
E unlinkLast() {
|
||||||
|
final E l = last;
|
||||||
|
final E prev = l.getPrevious();
|
||||||
|
l.setPrevious(null);
|
||||||
|
last = prev;
|
||||||
|
if (prev == null) {
|
||||||
|
first = null;
|
||||||
|
} else {
|
||||||
|
prev.setNext(null);
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Unlinks the non-null element. */
|
||||||
|
void unlink(E e) {
|
||||||
|
final E prev = e.getPrevious();
|
||||||
|
final E next = e.getNext();
|
||||||
|
|
||||||
|
if (prev == null) {
|
||||||
|
first = next;
|
||||||
|
} else {
|
||||||
|
prev.setNext(next);
|
||||||
|
e.setPrevious(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next == null) {
|
||||||
|
last = prev;
|
||||||
|
} else {
|
||||||
|
next.setPrevious(prev);
|
||||||
|
e.setNext(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return (first == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkNotEmpty() {
|
||||||
|
if (isEmpty()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* <p>
|
||||||
|
* Beware that, unlike in most collections, this method is <em>NOT</em> a
|
||||||
|
* constant-time operation.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
int size = 0;
|
||||||
|
for (E e = first; e != null; e = e.getNext()) {
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
for (E e = first; e != null;) {
|
||||||
|
E next = e.getNext();
|
||||||
|
e.setPrevious(null);
|
||||||
|
e.setNext(null);
|
||||||
|
e = next;
|
||||||
|
}
|
||||||
|
first = last = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return (o instanceof Linked<?>) && contains((Linked<?>) o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A fast-path containment check
|
||||||
|
boolean contains(Linked<?> e) {
|
||||||
|
return (e.getPrevious() != null)
|
||||||
|
|| (e.getNext() != null)
|
||||||
|
|| (e == first);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the element to the front of the deque so that it becomes the first
|
||||||
|
* element.
|
||||||
|
*
|
||||||
|
* @param e the linked element
|
||||||
|
*/
|
||||||
|
public void moveToFront(E e) {
|
||||||
|
if (e != first) {
|
||||||
|
unlink(e);
|
||||||
|
linkFirst(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the element to the back of the deque so that it becomes the last
|
||||||
|
* element.
|
||||||
|
*
|
||||||
|
* @param e the linked element
|
||||||
|
*/
|
||||||
|
public void moveToBack(E e) {
|
||||||
|
if (e != last) {
|
||||||
|
unlink(e);
|
||||||
|
linkLast(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E peek() {
|
||||||
|
return peekFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E peekFirst() {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E peekLast() {
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E getFirst() {
|
||||||
|
checkNotEmpty();
|
||||||
|
return peekFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E getLast() {
|
||||||
|
checkNotEmpty();
|
||||||
|
return peekLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E element() {
|
||||||
|
return getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean offer(E e) {
|
||||||
|
return offerLast(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean offerFirst(E e) {
|
||||||
|
if (contains(e)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
linkFirst(e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean offerLast(E e) {
|
||||||
|
if (contains(e)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
linkLast(e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(E e) {
|
||||||
|
return offerLast(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addFirst(E e) {
|
||||||
|
if (!offerFirst(e)) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLast(E e) {
|
||||||
|
if (!offerLast(e)) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E poll() {
|
||||||
|
return pollFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E pollFirst() {
|
||||||
|
return isEmpty() ? null : unlinkFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E pollLast() {
|
||||||
|
return isEmpty() ? null : unlinkLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E remove() {
|
||||||
|
return removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
return (o instanceof Linked<?>) && remove((E) o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A fast-path removal
|
||||||
|
boolean remove(E e) {
|
||||||
|
if (contains(e)) {
|
||||||
|
unlink(e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E removeFirst() {
|
||||||
|
checkNotEmpty();
|
||||||
|
return pollFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeFirstOccurrence(Object o) {
|
||||||
|
return remove(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E removeLast() {
|
||||||
|
checkNotEmpty();
|
||||||
|
return pollLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeLastOccurrence(Object o) {
|
||||||
|
return remove(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> c) {
|
||||||
|
boolean modified = false;
|
||||||
|
for (Object o : c) {
|
||||||
|
modified |= remove(o);
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void push(E e) {
|
||||||
|
addFirst(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E pop() {
|
||||||
|
return removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return new AbstractLinkedIterator(first) {
|
||||||
|
@Override E computeNext() {
|
||||||
|
return cursor.getNext();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> descendingIterator() {
|
||||||
|
return new AbstractLinkedIterator(last) {
|
||||||
|
@Override E computeNext() {
|
||||||
|
return cursor.getPrevious();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractLinkedIterator implements Iterator<E> {
|
||||||
|
E cursor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an iterator that can can traverse the deque.
|
||||||
|
*
|
||||||
|
* @param start the initial element to begin traversal from
|
||||||
|
*/
|
||||||
|
AbstractLinkedIterator(E start) {
|
||||||
|
cursor = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return (cursor != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E next() {
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
E e = cursor;
|
||||||
|
cursor = computeNext();
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the next element to traverse to or <tt>null</tt> if there are
|
||||||
|
* no more elements.
|
||||||
|
*/
|
||||||
|
abstract E computeNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An element that is linked on the {@link Deque}.
|
||||||
|
*/
|
||||||
|
interface Linked<T extends Linked<T>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the previous element or <tt>null</tt> if either the element is
|
||||||
|
* unlinked or the first element on the deque.
|
||||||
|
*/
|
||||||
|
T getPrevious();
|
||||||
|
|
||||||
|
/** Sets the previous element or <tt>null</tt> if there is no link. */
|
||||||
|
void setPrevious(T prev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the next element or <tt>null</tt> if either the element is
|
||||||
|
* unlinked or the last element on the deque.
|
||||||
|
*/
|
||||||
|
T getNext();
|
||||||
|
|
||||||
|
/** Sets the next element or <tt>null</tt> if there is no link. */
|
||||||
|
void setNext(T next);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.googlecode.concurrentlinkedhashmap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that can determine the weight of a value. The total weight threshold
|
||||||
|
* is used to determine when an eviction is required.
|
||||||
|
*
|
||||||
|
* @author ben.manes@gmail.com (Ben Manes)
|
||||||
|
* @see <a href="http://code.google.com/p/concurrentlinkedhashmap/">
|
||||||
|
* http://code.google.com/p/concurrentlinkedhashmap/</a>
|
||||||
|
*/
|
||||||
|
public interface Weigher<V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Measures an object's weight to determine how many units of capacity that
|
||||||
|
* the value consumes. A value must consume a minimum of one unit.
|
||||||
|
*
|
||||||
|
* @param value the object to weigh
|
||||||
|
* @return the object's weight
|
||||||
|
*/
|
||||||
|
int weightOf(V value);
|
||||||
|
}
|
|
@ -0,0 +1,282 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.googlecode.concurrentlinkedhashmap;
|
||||||
|
|
||||||
|
import static com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap.checkNotNull;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A common set of {@link Weigher} and {@link EntryWeigher} implementations.
|
||||||
|
*
|
||||||
|
* @author ben.manes@gmail.com (Ben Manes)
|
||||||
|
* @see <a href="http://code.google.com/p/concurrentlinkedhashmap/">
|
||||||
|
* http://code.google.com/p/concurrentlinkedhashmap/</a>
|
||||||
|
*/
|
||||||
|
public final class Weighers {
|
||||||
|
|
||||||
|
private Weighers() {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A entry weigher backed by the specified weigher. The weight of the value
|
||||||
|
* determines the weight of the entry.
|
||||||
|
*
|
||||||
|
* @param weigher the weigher to be "wrapped" in a entry weigher.
|
||||||
|
* @return A entry weigher view of the specified weigher.
|
||||||
|
*/
|
||||||
|
public static <K, V> EntryWeigher<K, V> asEntryWeigher(
|
||||||
|
final Weigher<? super V> weigher) {
|
||||||
|
return (weigher == singleton())
|
||||||
|
? Weighers.<K, V>entrySingleton()
|
||||||
|
: new EntryWeigherView<K, V>(weigher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A weigher where an entry has a weight of <tt>1</tt>. A map bounded with
|
||||||
|
* this weigher will evict when the number of key-value pairs exceeds the
|
||||||
|
* capacity.
|
||||||
|
*
|
||||||
|
* @return A weigher where a value takes one unit of capacity.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"cast", "unchecked"})
|
||||||
|
public static <K, V> EntryWeigher<K, V> entrySingleton() {
|
||||||
|
return (EntryWeigher<K, V>) SingletonEntryWeigher.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A weigher where a value has a weight of <tt>1</tt>. A map bounded with
|
||||||
|
* this weigher will evict when the number of key-value pairs exceeds the
|
||||||
|
* capacity.
|
||||||
|
*
|
||||||
|
* @return A weigher where a value takes one unit of capacity.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"cast", "unchecked"})
|
||||||
|
public static <V> Weigher<V> singleton() {
|
||||||
|
return (Weigher<V>) SingletonWeigher.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A weigher where the value is a byte array and its weight is the number of
|
||||||
|
* bytes. A map bounded with this weigher will evict when the number of bytes
|
||||||
|
* exceeds the capacity rather than the number of key-value pairs in the map.
|
||||||
|
* This allows for restricting the capacity based on the memory-consumption
|
||||||
|
* and is primarily for usage by dedicated caching servers that hold the
|
||||||
|
* serialized data.
|
||||||
|
* <p>
|
||||||
|
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
||||||
|
* with this weight can occur then the caller should eagerly evaluate the
|
||||||
|
* value and treat it as a removal operation. Alternatively, a custom weigher
|
||||||
|
* may be specified on the map to assign an empty value a positive weight.
|
||||||
|
*
|
||||||
|
* @return A weigher where each byte takes one unit of capacity.
|
||||||
|
*/
|
||||||
|
public static Weigher<byte[]> byteArray() {
|
||||||
|
return ByteArrayWeigher.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A weigher where the value is a {@link Iterable} and its weight is the
|
||||||
|
* number of elements. This weigher only should be used when the alternative
|
||||||
|
* {@link #collection()} weigher cannot be, as evaluation takes O(n) time. A
|
||||||
|
* map bounded with this weigher will evict when the total number of elements
|
||||||
|
* exceeds the capacity rather than the number of key-value pairs in the map.
|
||||||
|
* <p>
|
||||||
|
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
||||||
|
* with this weight can occur then the caller should eagerly evaluate the
|
||||||
|
* value and treat it as a removal operation. Alternatively, a custom weigher
|
||||||
|
* may be specified on the map to assign an empty value a positive weight.
|
||||||
|
*
|
||||||
|
* @return A weigher where each element takes one unit of capacity.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"cast", "unchecked"})
|
||||||
|
public static <E> Weigher<? super Iterable<E>> iterable() {
|
||||||
|
return (Weigher<Iterable<E>>) (Weigher<?>) IterableWeigher.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A weigher where the value is a {@link Collection} and its weight is the
|
||||||
|
* number of elements. A map bounded with this weigher will evict when the
|
||||||
|
* total number of elements exceeds the capacity rather than the number of
|
||||||
|
* key-value pairs in the map.
|
||||||
|
* <p>
|
||||||
|
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
||||||
|
* with this weight can occur then the caller should eagerly evaluate the
|
||||||
|
* value and treat it as a removal operation. Alternatively, a custom weigher
|
||||||
|
* may be specified on the map to assign an empty value a positive weight.
|
||||||
|
*
|
||||||
|
* @return A weigher where each element takes one unit of capacity.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"cast", "unchecked"})
|
||||||
|
public static <E> Weigher<? super Collection<E>> collection() {
|
||||||
|
return (Weigher<Collection<E>>) (Weigher<?>) CollectionWeigher.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A weigher where the value is a {@link List} and its weight is the number
|
||||||
|
* of elements. A map bounded with this weigher will evict when the total
|
||||||
|
* number of elements exceeds the capacity rather than the number of
|
||||||
|
* key-value pairs in the map.
|
||||||
|
* <p>
|
||||||
|
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
||||||
|
* with this weight can occur then the caller should eagerly evaluate the
|
||||||
|
* value and treat it as a removal operation. Alternatively, a custom weigher
|
||||||
|
* may be specified on the map to assign an empty value a positive weight.
|
||||||
|
*
|
||||||
|
* @return A weigher where each element takes one unit of capacity.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"cast", "unchecked"})
|
||||||
|
public static <E> Weigher<? super List<E>> list() {
|
||||||
|
return (Weigher<List<E>>) (Weigher<?>) ListWeigher.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A weigher where the value is a {@link Set} and its weight is the number
|
||||||
|
* of elements. A map bounded with this weigher will evict when the total
|
||||||
|
* number of elements exceeds the capacity rather than the number of
|
||||||
|
* key-value pairs in the map.
|
||||||
|
* <p>
|
||||||
|
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
||||||
|
* with this weight can occur then the caller should eagerly evaluate the
|
||||||
|
* value and treat it as a removal operation. Alternatively, a custom weigher
|
||||||
|
* may be specified on the map to assign an empty value a positive weight.
|
||||||
|
*
|
||||||
|
* @return A weigher where each element takes one unit of capacity.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"cast", "unchecked"})
|
||||||
|
public static <E> Weigher<? super Set<E>> set() {
|
||||||
|
return (Weigher<Set<E>>) (Weigher<?>) SetWeigher.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A weigher where the value is a {@link Map} and its weight is the number of
|
||||||
|
* entries. A map bounded with this weigher will evict when the total number of
|
||||||
|
* entries across all values exceeds the capacity rather than the number of
|
||||||
|
* key-value pairs in the map.
|
||||||
|
* <p>
|
||||||
|
* A value with a weight of <tt>0</tt> will be rejected by the map. If a value
|
||||||
|
* with this weight can occur then the caller should eagerly evaluate the
|
||||||
|
* value and treat it as a removal operation. Alternatively, a custom weigher
|
||||||
|
* may be specified on the map to assign an empty value a positive weight.
|
||||||
|
*
|
||||||
|
* @return A weigher where each entry takes one unit of capacity.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"cast", "unchecked"})
|
||||||
|
public static <A, B> Weigher<? super Map<A, B>> map() {
|
||||||
|
return (Weigher<Map<A, B>>) (Weigher<?>) MapWeigher.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class EntryWeigherView<K, V> implements EntryWeigher<K, V>, Serializable {
|
||||||
|
static final long serialVersionUID = 1;
|
||||||
|
final Weigher<? super V> weigher;
|
||||||
|
|
||||||
|
EntryWeigherView(Weigher<? super V> weigher) {
|
||||||
|
checkNotNull(weigher);
|
||||||
|
this.weigher = weigher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int weightOf(K key, V value) {
|
||||||
|
return weigher.weightOf(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SingletonEntryWeigher implements EntryWeigher<Object, Object> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int weightOf(Object key, Object value) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SingletonWeigher implements Weigher<Object> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int weightOf(Object value) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ByteArrayWeigher implements Weigher<byte[]> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int weightOf(byte[] value) {
|
||||||
|
return value.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum IterableWeigher implements Weigher<Iterable<?>> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int weightOf(Iterable<?> values) {
|
||||||
|
if (values instanceof Collection<?>) {
|
||||||
|
return ((Collection<?>) values).size();
|
||||||
|
}
|
||||||
|
int size = 0;
|
||||||
|
for (Iterator<?> i = values.iterator(); i.hasNext();) {
|
||||||
|
i.next();
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CollectionWeigher implements Weigher<Collection<?>> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int weightOf(Collection<?> values) {
|
||||||
|
return values.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ListWeigher implements Weigher<List<?>> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int weightOf(List<?> values) {
|
||||||
|
return values.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SetWeigher implements Weigher<Set<?>> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int weightOf(Set<?> values) {
|
||||||
|
return values.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MapWeigher implements Weigher<Map<?, ?>> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int weightOf(Map<?, ?> values) {
|
||||||
|
return values.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains an implementation of a bounded
|
||||||
|
* {@link java.util.concurrent.ConcurrentMap} data structure.
|
||||||
|
* <p>
|
||||||
|
* {@link com.googlecode.concurrentlinkedhashmap.Weigher} is a simple interface
|
||||||
|
* for determining how many units of capacity an entry consumes. Depending on
|
||||||
|
* which concrete Weigher class is used, an entry may consume a different amount
|
||||||
|
* of space within the cache. The
|
||||||
|
* {@link com.googlecode.concurrentlinkedhashmap.Weighers} class provides
|
||||||
|
* utility methods for obtaining the most common kinds of implementations.
|
||||||
|
* <p>
|
||||||
|
* {@link com.googlecode.concurrentlinkedhashmap.EvictionListener} provides the
|
||||||
|
* ability to be notified when an entry is evicted from the map. An eviction
|
||||||
|
* occurs when the entry was automatically removed due to the map exceeding a
|
||||||
|
* capacity threshold. It is not called when an entry was explicitly removed.
|
||||||
|
* <p>
|
||||||
|
* The {@link com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap}
|
||||||
|
* class supplies an efficient, scalable, thread-safe, bounded map. As with the
|
||||||
|
* <tt>Java Collections Framework</tt> the "Concurrent" prefix is used to
|
||||||
|
* indicate that the map is not governed by a single exclusion lock.
|
||||||
|
*
|
||||||
|
* @see <a href="http://code.google.com/p/concurrentlinkedhashmap/">
|
||||||
|
* http://code.google.com/p/concurrentlinkedhashmap/</a>
|
||||||
|
*/
|
||||||
|
package com.googlecode.concurrentlinkedhashmap;
|
|
@ -35,6 +35,8 @@ import android.widget.ArrayAdapter;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
|
||||||
|
|
||||||
import net.osmand.AndroidUtils;
|
import net.osmand.AndroidUtils;
|
||||||
import net.osmand.IProgress;
|
import net.osmand.IProgress;
|
||||||
import net.osmand.IndexConstants;
|
import net.osmand.IndexConstants;
|
||||||
|
@ -75,10 +77,8 @@ import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -130,13 +130,17 @@ public class AudioVideoNotesPlugin extends OsmandPlugin {
|
||||||
public final OsmandPreference<Boolean> SHOW_RECORDINGS;
|
public final OsmandPreference<Boolean> SHOW_RECORDINGS;
|
||||||
|
|
||||||
private DataTileManager<Recording> recordings = new DataTileManager<AudioVideoNotesPlugin.Recording>(14);
|
private DataTileManager<Recording> recordings = new DataTileManager<AudioVideoNotesPlugin.Recording>(14);
|
||||||
private Map<String, Recording> recordingByFileName = Collections.synchronizedMap(new LinkedHashMap<String, Recording>());
|
private Map<String, Recording> recordingByFileName =
|
||||||
|
new ConcurrentLinkedHashMap.Builder<String, Recording>()
|
||||||
|
.maximumWeightedCapacity(1000)
|
||||||
|
.build();
|
||||||
private AudioNotesLayer audioNotesLayer;
|
private AudioNotesLayer audioNotesLayer;
|
||||||
private MapActivity activity;
|
private MapActivity activity;
|
||||||
private MediaRecorder mediaRec;
|
private MediaRecorder mediaRec;
|
||||||
private File lastTakingPhoto;
|
private File lastTakingPhoto;
|
||||||
|
|
||||||
private final static char SPLIT_DESC = ' ';
|
private final static char SPLIT_DESC = ' ';
|
||||||
|
|
||||||
public static class Recording {
|
public static class Recording {
|
||||||
public Recording(File f) {
|
public Recording(File f) {
|
||||||
this.file = f;
|
this.file = f;
|
||||||
|
@ -198,11 +202,11 @@ public class AudioVideoNotesPlugin extends OsmandPlugin {
|
||||||
public String getDescriptionName(String fileName) {
|
public String getDescriptionName(String fileName) {
|
||||||
int hashInd = fileName.lastIndexOf(SPLIT_DESC);
|
int hashInd = fileName.lastIndexOf(SPLIT_DESC);
|
||||||
//backward compatibility
|
//backward compatibility
|
||||||
if( fileName.indexOf('.') - fileName.indexOf('_') > 12 &&
|
if (fileName.indexOf('.') - fileName.indexOf('_') > 12 &&
|
||||||
hashInd < fileName.indexOf('_')) {
|
hashInd < fileName.indexOf('_')) {
|
||||||
hashInd = fileName.indexOf('_');
|
hashInd = fileName.indexOf('_');
|
||||||
}
|
}
|
||||||
if(hashInd == -1) {
|
if (hashInd == -1) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return fileName.substring(0, hashInd);
|
return fileName.substring(0, hashInd);
|
||||||
|
@ -211,7 +215,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin {
|
||||||
|
|
||||||
public String getOtherName(String fileName) {
|
public String getOtherName(String fileName) {
|
||||||
String descriptionName = getDescriptionName(fileName);
|
String descriptionName = getDescriptionName(fileName);
|
||||||
if(descriptionName != null) {
|
if (descriptionName != null) {
|
||||||
return fileName.substring(descriptionName.length() + 1); // SPLIT_DESC
|
return fileName.substring(descriptionName.length() + 1); // SPLIT_DESC
|
||||||
} else {
|
} else {
|
||||||
return fileName;
|
return fileName;
|
||||||
|
@ -233,8 +237,8 @@ public class AudioVideoNotesPlugin extends OsmandPlugin {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSearchHistoryType(){
|
public String getSearchHistoryType() {
|
||||||
if (isPhoto()){
|
if (isPhoto()) {
|
||||||
return PointDescription.POINT_TYPE_PHOTO_NOTE;
|
return PointDescription.POINT_TYPE_PHOTO_NOTE;
|
||||||
} else if (isVideo()) {
|
} else if (isVideo()) {
|
||||||
return PointDescription.POINT_TYPE_VIDEO_NOTE;
|
return PointDescription.POINT_TYPE_VIDEO_NOTE;
|
||||||
|
@ -445,7 +449,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin {
|
||||||
}
|
}
|
||||||
audioNotesLayer = new AudioNotesLayer(activity, this);
|
audioNotesLayer = new AudioNotesLayer(activity, this);
|
||||||
activity.getMapView().addLayer(audioNotesLayer, 3.5f);
|
activity.getMapView().addLayer(audioNotesLayer, 3.5f);
|
||||||
if(recordControl == null) {
|
if (recordControl == null) {
|
||||||
registerWidget(activity);
|
registerWidget(activity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -535,7 +539,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin {
|
||||||
} else if (audioNotesLayer != null) {
|
} else if (audioNotesLayer != null) {
|
||||||
mapView.removeLayer(audioNotesLayer);
|
mapView.removeLayer(audioNotesLayer);
|
||||||
}
|
}
|
||||||
if(recordControl == null) {
|
if (recordControl == null) {
|
||||||
registerWidget(activity);
|
registerWidget(activity);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -544,7 +548,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin {
|
||||||
audioNotesLayer = null;
|
audioNotesLayer = null;
|
||||||
}
|
}
|
||||||
MapInfoLayer mapInfoLayer = activity.getMapLayers().getMapInfoLayer();
|
MapInfoLayer mapInfoLayer = activity.getMapLayers().getMapInfoLayer();
|
||||||
if(recordControl != null && mapInfoLayer != null) {
|
if (recordControl != null && mapInfoLayer != null) {
|
||||||
mapInfoLayer.removeSideWidget(recordControl);
|
mapInfoLayer.removeSideWidget(recordControl);
|
||||||
recordControl = null;
|
recordControl = null;
|
||||||
mapInfoLayer.recreateControls();
|
mapInfoLayer.recreateControls();
|
||||||
|
|
Loading…
Reference in a new issue