| /* |
| * 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.kafka.common.utils; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| /** |
| * A memory-efficient hash multiset which tracks the order of insertion of elements. |
| * See org.apache.kafka.common.utils.ImplicitLinkedHashCollection for implementation details. |
| * |
| * This class is a multi-set because it allows multiple elements to be inserted that are |
| * equal to each other. |
| * |
| * We use reference equality when adding elements to the set. A new element A can |
| * be added if there is no existing element B such that A == B. If an element B |
| * exists such that A.equals(B), A will still be added. |
| * |
| * When deleting an element A from the set, we will try to delete the element B such |
| * that A == B. If no such element can be found, we will try to delete an element B |
| * such that A.equals(B). |
| * |
| * contains() and find() are unchanged from the base class-- they will look for element |
| * based on object equality, not reference equality. |
| * |
| * This multiset does not allow null elements. It does not have internal synchronization. |
| */ |
| public class ImplicitLinkedHashMultiCollection<E extends ImplicitLinkedHashCollection.Element> |
| extends ImplicitLinkedHashCollection<E> { |
| public ImplicitLinkedHashMultiCollection() { |
| super(0); |
| } |
| |
| public ImplicitLinkedHashMultiCollection(int expectedNumElements) { |
| super(expectedNumElements); |
| } |
| |
| public ImplicitLinkedHashMultiCollection(Iterator<E> iter) { |
| super(iter); |
| } |
| |
| |
| /** |
| * Adds a new element to the appropriate place in the elements array. |
| * |
| * @param newElement The new element to add. |
| * @param addElements The elements array. |
| * @return The index at which the element was inserted, or INVALID_INDEX |
| * if the element could not be inserted. |
| */ |
| @Override |
| int addInternal(Element newElement, Element[] addElements) { |
| int slot = slot(addElements, newElement); |
| for (int seen = 0; seen < addElements.length; seen++) { |
| Element element = addElements[slot]; |
| if (element == null) { |
| addElements[slot] = newElement; |
| return slot; |
| } |
| if (element == newElement) { |
| return INVALID_INDEX; |
| } |
| slot = (slot + 1) % addElements.length; |
| } |
| throw new RuntimeException("Not enough hash table slots to add a new element."); |
| } |
| |
| /** |
| * Find an element matching an example element. |
| * |
| * @param key The element to match. |
| * |
| * @return The match index, or INVALID_INDEX if no match was found. |
| */ |
| @Override |
| int findElementToRemove(Object key) { |
| if (key == null || size() == 0) { |
| return INVALID_INDEX; |
| } |
| int slot = slot(elements, key); |
| int bestSlot = INVALID_INDEX; |
| for (int seen = 0; seen < elements.length; seen++) { |
| Element element = elements[slot]; |
| if (element == null) { |
| return bestSlot; |
| } |
| if (key == element) { |
| return slot; |
| } else if (key.equals(element)) { |
| bestSlot = slot; |
| } |
| slot = (slot + 1) % elements.length; |
| } |
| return INVALID_INDEX; |
| } |
| |
| /** |
| * Returns all of the elements e in the collection such that |
| * key.equals(e) and key.hashCode() == e.hashCode(). |
| * |
| * @param key The element to match. |
| * |
| * @return All of the matching elements. |
| */ |
| final public List<E> findAll(E key) { |
| if (key == null || size() == 0) { |
| return Collections.<E>emptyList(); |
| } |
| ArrayList<E> results = new ArrayList<>(); |
| int slot = slot(elements, key); |
| for (int seen = 0; seen < elements.length; seen++) { |
| Element element = elements[slot]; |
| if (element == null) { |
| break; |
| } |
| if (key.equals(element)) { |
| @SuppressWarnings("unchecked") |
| E result = (E) elements[slot]; |
| results.add(result); |
| } |
| slot = (slot + 1) % elements.length; |
| } |
| return results; |
| } |
| } |