001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.hbase.util;
020
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.List;
026import java.util.concurrent.ConcurrentMap;
027import java.util.function.Supplier;
028
029import org.apache.yetus.audience.InterfaceAudience;
030
031/**
032 * Utility methods for dealing with Collections, including treating null collections as empty.
033 */
034@InterfaceAudience.Private
035public class CollectionUtils {
036
037  private static final List<Object> EMPTY_LIST = Collections.unmodifiableList(new ArrayList<>(0));
038
039  
040  @SuppressWarnings("unchecked")
041  public static <T> Collection<T> nullSafe(Collection<T> in) {
042    if (in == null) {
043      return (Collection<T>)EMPTY_LIST;
044    }
045    return in;
046  }
047
048  /************************ size ************************************/
049
050  public static <T> int nullSafeSize(Collection<T> collection) {
051    if (collection == null) {
052      return 0;
053    }
054    return collection.size();
055  }
056
057  public static <A, B> boolean nullSafeSameSize(Collection<A> a, Collection<B> b) {
058    return nullSafeSize(a) == nullSafeSize(b);
059  }
060
061  /*************************** empty ****************************************/
062
063  public static <T> boolean isEmpty(Collection<T> collection) {
064    return collection == null || collection.isEmpty();
065  }
066
067  public static <T> boolean notEmpty(Collection<T> collection) {
068    return !isEmpty(collection);
069  }
070
071  /************************ first/last **************************/
072
073  public static <T> T getFirst(Collection<T> collection) {
074    if (CollectionUtils.isEmpty(collection)) {
075      return null;
076    }
077    for (T t : collection) {
078      return t;
079    }
080    return null;
081  }
082  
083  /**
084   * @param list any list
085   * @return -1 if list is empty, otherwise the max index
086   */
087  public static int getLastIndex(List<?> list){
088    if(isEmpty(list)){
089      return -1;
090    }
091    return list.size() - 1;
092  }
093  
094  /**
095   * @param list
096   * @param index the index in question
097   * @return true if it is the last index or if list is empty and -1 is passed for the index param
098   */
099  public static boolean isLastIndex(List<?> list, int index){
100    return index == getLastIndex(list);
101  }
102
103  public static <T> T getLast(List<T> list) {
104    if (isEmpty(list)) {
105      return null;
106    }
107    return list.get(list.size() - 1);
108  }
109
110  /**
111   * In HBASE-16648 we found that ConcurrentHashMap.get is much faster than computeIfAbsent if the
112   * value already exists. Notice that the implementation does not guarantee that the supplier will
113   * only be executed once.
114   */
115  public static <K, V> V computeIfAbsent(ConcurrentMap<K, V> map, K key, Supplier<V> supplier) {
116    return computeIfAbsent(map, key, supplier, () -> {
117    });
118  }
119
120  /**
121   * A supplier that throws IOException when get.
122   */
123  @FunctionalInterface
124  public interface IOExceptionSupplier<V> {
125    V get() throws IOException;
126  }
127
128  /**
129   * In HBASE-16648 we found that ConcurrentHashMap.get is much faster than computeIfAbsent if the
130   * value already exists. So here we copy the implementation of
131   * {@link ConcurrentMap#computeIfAbsent(Object, java.util.function.Function)}. It uses get and
132   * putIfAbsent to implement computeIfAbsent. And notice that the implementation does not guarantee
133   * that the supplier will only be executed once.
134   */
135  public static <K, V> V computeIfAbsentEx(ConcurrentMap<K, V> map, K key,
136      IOExceptionSupplier<V> supplier) throws IOException {
137    V v, newValue;
138    return ((v = map.get(key)) == null && (newValue = supplier.get()) != null
139        && (v = map.putIfAbsent(key, newValue)) == null) ? newValue : v;
140  }
141
142  public static <K, V> V computeIfAbsent(ConcurrentMap<K, V> map, K key, Supplier<V> supplier,
143      Runnable actionIfAbsent) {
144    V v = map.get(key);
145    if (v != null) {
146      return v;
147    }
148    V newValue = supplier.get();
149    v = map.putIfAbsent(key, newValue);
150    if (v != null) {
151      return v;
152    }
153    actionIfAbsent.run();
154    return newValue;
155  }
156}