Google Guava — это библиотека с расширенным набором утилит для Java, которая упрощает многие аспекты разработки. Она предлагает множество полезных классов и методов для работы с коллекциями, кэшированием, функциональным программированием, обработкой строк и многого другого.
Основные возможности Google Guava:
Коллекции:
Guava значительно расширяет стандартные коллекции Java, предоставляя новые структуры данных и утилиты для работы с ними.
Multimap:
Коллекция, которая позволяет хранить несколько значений для одного ключа.
Примеры: ArrayListMultimap, HashMultimap, LinkedListMultimap.
import com.google.common.collect.ArrayListMultimap import com.google.common.collect.Multimap fun multimapExample() { val multimap: Multimap<String, String> = ArrayListMultimap.create() multimap.put("fruit", "apple") multimap.put("fruit", "banana") multimap.put("vegetable", "carrot") println(multimap.get("fruit")) // [apple, banana] println(multimap.get("vegetable")) // [carrot] }
Multiset:
Коллекция, которая позволяет хранить несколько копий одного и того же элемента.
Примеры: HashMultiset, TreeMultiset.
import com.google.common.collect.HashMultiset import com.google.common.collect.Multiset fun multisetExample() { val multiset: Multiset<String> = HashMultiset.create() multiset.add("apple") multiset.add("apple") multiset.add("banana") println(multiset.count("apple")) // 2 println(multiset.count("banana")) // 1 }
BiMap:
Двунаправленная Map, где каждому ключу соответствует уникальное значение, и наоборот.
Пример: HashBiMap.
import com.google.common.collect.HashBiMap fun bimapExample() { val biMap = HashBiMap.create<String, String>() biMap["one"] = "1" biMap["two"] = "2" // Доступ к значению по ключу println(biMap["one"]) // 1 // Обратный доступ к ключу по значению println(biMap.inverse()["1"]) // one }
Table:
Двумерная Map, эквивалентная Map>.
Пример: HashBasedTable.
import com.google.common.collect.HashBasedTable import com.google.common.collect.Table fun tableExample() { val table: Table<String, String, Int> = HashBasedTable.create() table.put("row1", "column1", 1) table.put("row1", "column2", 2) table.put("row2", "column1", 3) println(table.get("row1", "column1")) // 1 println(table.get("row1", "column2")) // 2 println(table.row("row1")) // {column1=1, column2=2} println(table.column("column1")) // {row1=1, row2=3} }
ClassToInstanceMap:
Map, где ключи — это классы, а значения — экземпляры этих классов.
Пример: MutableClassToInstanceMap.
import com.google.common.collect.MutableClassToInstanceMap import com.google.common.collect.ClassToInstanceMap fun classToInstanceMapExample() { val classToInstanceMap: ClassToInstanceMap<Any> = MutableClassToInstanceMap.create() classToInstanceMap.putInstance(String::class.java, "Hello World") classToInstanceMap.putInstance(Int::class.java, 42) val strValue = classToInstanceMap.getInstance(String::class.java) val intValue = classToInstanceMap.getInstance(Int::class.java) println(strValue) // Hello World println(intValue) // 42 }
RangeSet:
Набор диапазонов чисел, предоставляющий операции для проверки пересечения и включения.
Пример: TreeRangeSet.
import com.google.common.collect.Range import com.google.common.collect.TreeRangeSet fun rangeSetExample() { val rangeSet = TreeRangeSet.create<Int>() rangeSet.add(Range.closed(1, 10)) rangeSet.add(Range.closed(20, 30)) println(rangeSet.contains(5)) // true println(rangeSet.contains(15)) // false println(rangeSet.contains(25)) // true }
RangeMap:
Map, ключами которого являются диапазоны, а значениями — объекты, ассоциированные с этими диапазонами.
Пример: TreeRangeMap.
import com.google.common.collect.Range import com.google.common.collect.TreeRangeMap fun rangeMapExample() { val rangeMap = TreeRangeMap.create<Int, String>() rangeMap.put(Range.closed(1, 10), "Low") rangeMap.put(Range.closed(20, 30), "Medium") rangeMap.put(Range.closed(31, 40), "High") println(rangeMap.get(5)) // Low println(rangeMap.get(25)) // Medium println(rangeMap.get(35)) // High }
Immutable Collections:
Неизменяемые версии стандартных коллекций для повышения безопасности и оптимизации.
Примеры: ImmutableList, ImmutableSet, ImmutableMap, ImmutableMultimap, ImmutableTable.
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableSet fun immutableCollectionsExample() { val immutableList: List<String> = ImmutableList.of("apple", "banana", "cherry") val immutableSet: Set<String> = ImmutableSet.of("apple", "banana", "cherry") println(immutableList) // [apple, banana, cherry] println(immutableSet) // [apple, banana, cherry] // Эти коллекции неизменяемы, попытка добавить элемент вызовет ошибку // immutableList.add("pear") // UnsupportedOperationException }
Работа со строками:
Splitter:
Утилита для разбиения строки по разделителю с различными опциями (пропуск пустых строк, обрезка пробелов и т. д.).
import com.google.common.base.Splitter fun splitterExample() { val input = "apple, banana, , orange,," val result = Splitter.on(',') .trimResults() // Обрезаем пробелы .omitEmptyStrings() // Пропускаем пустые строки .splitToList(input) println(result) // [apple, banana, orange] }
Joiner:
Утилита для объединения коллекции строк или других объектов в строку с указанным разделителем.
import com.google.common.base.Joiner fun joinerExample() { val fruits = listOf("apple", null, "banana", "orange") // Joiner будет пропускать null значения val result = Joiner.on(", ") .skipNulls() .join(fruits) println(result) // apple, banana, orange }
CharMatcher:
Утилита для поиска и манипуляций с символами в строке.
import com.google.common.base.CharMatcher fun charMatcherExample() { val input = "Hello123 World456" // Удаляем все цифры val onlyLetters = CharMatcher.javaDigit().removeFrom(input) println(onlyLetters) // Hello World // Оставляем только цифры val onlyDigits = CharMatcher.javaDigit().retainFrom(input) println(onlyDigits) // 123456 // Обрезаем пробелы и заменяем их на запятые val trimmed = CharMatcher.whitespace().trimAndCollapseFrom(input, ',') println(trimmed) // Hello123,World456 }
CaseFormat:
Преобразование между различными форматами регистров строк (например, snake_case, camelCase, UPPER_UNDERSCORE и др.).
import com.google.common.base.CaseFormat fun caseFormatExample() { val input = "my_variable_name" // Преобразуем из snake_case в camelCase val camelCase = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input) println(camelCase) // myVariableName // Преобразуем из snake_case в UPPER_UNDERSCORE val upperUnderscore = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, input) println(upperUnderscore) // MY_VARIABLE_NAME }
Кэширование:
Guava предоставляет мощный механизм кэширования через классы Cache и LoadingCache, который поддерживает различные стратегии удаления данных, такие как удаление по времени жизни или по количеству элементов
Cache:
Встроенная реализация кэша с поддержкой автоматического удаления данных по времени жизни или объему.
import com.google.common.cache.CacheBuilder import com.google.common.cache.Cache fun simpleCacheExample() { // Создаем кэш с максимальным количеством элементов 100 и временем жизни 10 минут val cache: Cache<String, String> = CacheBuilder.newBuilder() .maximumSize(100) .expireAfterWrite(10, java.util.concurrent.TimeUnit.MINUTES) .build() // Добавляем данные в кэш cache.put("key1", "value1") // Получаем данные из кэша val value = cache.getIfPresent("key1") println(value) // value1 // Если ключ отсутствует, возвращается null val missingValue = cache.getIfPresent("key2") println(missingValue) // null }
LoadingCache:
Кэш, который автоматически загружает данные, если их нет, с помощью функции загрузки.
import com.google.common.cache.CacheBuilder import com.google.common.cache.CacheLoader import com.google.common.cache.LoadingCache import java.util.concurrent.TimeUnit fun loadingCacheExample() { // Создаем кэш, который автоматически загружает данные, если их нет val loadingCache: LoadingCache<String, String> = CacheBuilder.newBuilder() .maximumSize(100) .expireAfterWrite(10, TimeUnit.MINUTES) .build(object : CacheLoader<String, String>() { override fun load(key: String): String { return "Generated value for $key" } }) // Получаем данные из кэша, если ключ отсутствует — данные генерируются автоматически println(loadingCache.get("key1")) // Generated value for key1 // Добавляем элемент вручную в кэш loadingCache.put("key2", "Manually added value") println(loadingCache.get("key2")) // Manually added value }
RemovalListener:
Обработчик для событий удаления элементов из кэша.
import com.google.common.cache.CacheBuilder import com.google.common.cache.RemovalListener import com.google.common.cache.RemovalNotification import java.util.concurrent.TimeUnit fun cacheWithRemovalListenerExample() { // Создаем кэш с обработчиком удаления элементов val cache = CacheBuilder.newBuilder() .maximumSize(100) .expireAfterWrite(10, TimeUnit.MINUTES) .removalListener(object : RemovalListener<String, String> { override fun onRemoval(notification: RemovalNotification<String, String>) { println("Removed key: ${notification.key}, value: ${notification.value}, cause: ${notification.cause}") } }) .build<String, String>() // Добавляем и удаляем элементы из кэша cache.put("key1", "value1") cache.invalidate("key1") // Принудительное удаление элемента }
CacheBuilder:
Позволяет гибко настраивать кэш с ограничениями по времени жизни элементов, количеству, обработке конкуренции и т. д.
import com.google.common.cache.CacheBuilder import com.google.common.cache.Cache fun cacheBuilderExample() { // Создаем кэш с максимальным количеством элементов и временем жизни 5 минут val cache: Cache<String, String> = CacheBuilder.newBuilder() .maximumSize(200) // Ограничение по количеству элементов .expireAfterWrite(5, TimeUnit.MINUTES) // Элементы живут 5 минут .build() // Добавляем элемент cache.put("key1", "value1") // Получаем элемент println(cache.getIfPresent("key1")) // value1 // Через 5 минут элемент будет удален автоматически }
Функциональное программирование:
Predicate:
Интерфейс для определения логического условия для объектов.
import com.google.common.base.Predicate import com.google.common.collect.FluentIterable fun predicateExample() { val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // Определяем предикат для фильтрации четных чисел val isEven: Predicate<Int> = Predicate { it % 2 == 0 } // Используем FluentIterable для фильтрации чисел val evenNumbers = FluentIterable.from(numbers) .filter(isEven) .toList() println(evenNumbers) // [2, 4, 6, 8, 10] }
Function:
Интерфейс для преобразования одного объекта в другой.
import com.google.common.base.Function import com.google.common.collect.FluentIterable fun functionExample() { val numbers = listOf(1, 2, 3, 4, 5) // Определяем функцию для возведения числа в квадрат val squareFunction: Function<Int, Int> = Function { it * it } // Преобразуем список чисел val squaredNumbers = FluentIterable.from(numbers) .transform(squareFunction) .toList() println(squaredNumbers) // [1, 4, 9, 16, 25] }
Suppliers:
Утилита для ленивой и мемоизированной поставки значений.
import com.google.common.base.Suppliers import java.util.concurrent.TimeUnit fun supplierExample() { // Ленивый вычислитель строки с мемоизацией на 10 секунд val supplier = Suppliers.memoizeWithExpiration( Suppliers.ofInstance("Hello, world!"), 10, TimeUnit.SECONDS ) // Получаем значение println(supplier.get()) // Hello, world! // Значение кэшируется на 10 секунд, затем будет пересчитано }
Optional:
Контейнер для объектов, который может содержать либо значение, либо быть пустым (альтернатива null).
import com.google.common.base.Optional fun optionalExample() { val optionalPresent: Optional<String> = Optional.of("Hello") val optionalAbsent: Optional<String> = Optional.absent() // Проверка наличия значения if (optionalPresent.isPresent) { println(optionalPresent.get()) // Hello } // Возвращение значения по умолчанию, если его нет println(optionalAbsent.or("Default Value")) // Default Value }
FluentIterable:
Утилита для обработки коллекций с поддержкой методов функциональной композиции (фильтрация, преобразование и т. д.).
import com.google.common.collect.FluentIterable fun fluentIterableExample() { val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // Комбинируем фильтрацию и трансформацию с помощью FluentIterable val processedNumbers = FluentIterable.from(numbers) .filter { it % 2 == 0 } // Фильтруем только четные числа .transform { it * 2 } // Умножаем каждое на 2 .toList() println(processedNumbers) // [4, 8, 12, 16, 20] }
Работа с исключениями:
Throwables:
Утилиты для работы с исключениями (например, приведение checked исключений к runtime исключениям).
import com.google.common.base.Throwables fun throwablesExample() { try { // Искусственно создаем исключение throw IllegalArgumentException("Illegal argument!") } catch (e: Exception) { // Выводим полную информацию об исключении println(Throwables.getStackTraceAsString(e)) // Проверка и повторное выбрасывание исключения, если оно определенного типа if (Throwables.getRootCause(e) is IllegalArgumentException) { throw Throwables.propagate(e) } } }
Retryer:
Утилита для повторного выполнения операций при возникновении исключений с поддержкой стратегий ожидания и остановки. Google Guava не содержит встроенной поддержки Retryer, но есть популярные библиотеки, такие как Failsafe или Guava-Retrying, которые можно использовать для повторных попыток. Вот пример использования библиотеки Guava-Retrying для повторной попытки выполнения операции:
import com.github.rholder.retry.Retryer import com.github.rholder.retry.RetryerBuilder import com.github.rholder.retry.StopStrategies import com.github.rholder.retry.WaitStrategies import java.util.concurrent.TimeUnit fun retryerExample() { // Создаем Retryer с максимальным количеством попыток 3 и ожиданием 2 секунды между попытками val retryer: Retryer<String> = RetryerBuilder.newBuilder<String>() .retryIfException() // Повторять при возникновении любого исключения .withWaitStrategy(WaitStrategies.fixedWait(2, TimeUnit.SECONDS)) // Ждем 2 секунды между попытками .withStopStrategy(StopStrategies.stopAfterAttempt(3)) // Останавливаемся после 3 попыток .build() try { // Выполняем операцию с повторными попытками val result = retryer.call { // Логика, которая может выбросить исключение if (Math.random() > 0.7) { "Success" } else { throw RuntimeException("Failed attempt") } } println("Operation succeeded: $result") } catch (e: Exception) { println("Operation failed after retries: ${e.message}") } }
Checked exceptions to Unchecked exceptions:
Guava помогает в преобразовании checked-исключений в unchecked через Throwables.propagate():
import com.google.common.base.Throwables fun propagateExample() { try { throwCheckedException() } catch (e: Exception) { // Преобразуем checked-исключение в unchecked-исключение throw Throwables.propagate(e) } } fun throwCheckedException() { throw Exception("Checked exception!") }
Работа с примитивами:
Google Guava предлагает удобные утилиты для работы с примитивами, такие как Ints, Longs, Doubles, Booleans, Bytes, а также поддержку беззнаковых целых чисел и неизменяемых коллекций для примитивов. Эти утилиты позволяют избежать работы с обертками примитивных типов и выполнять различные операции более эффективно.
Ints, Longs, Doubles, Booleans, Floats, Bytes:
Утилиты для преобразования и работы с примитивными типами.
import com.google.common.primitives.Ints fun intsExample() { val numbers = listOf(1, 2, 3, 4, 5) // Преобразование списка в массив int val intArray = Ints.toArray(numbers) println(intArray.contentToString()) // [1, 2, 3, 4, 5] // Поиск максимального и минимального значения println(Ints.max(1, 3, 5, 2)) // 5 println(Ints.min(1, 3, 5, 2)) // 1 // Проверка наличия числа в массиве println(Ints.contains(intArray, 3)) // true // Сравнение двух int println(Ints.compare(5, 10)) // -1 (первый меньше второго) }
import com.google.common.primitives.Longs fun longsExample() { val longArray = longArrayOf(10L, 20L, 30L) // Преобразование массива long в список val longList = Longs.asList(*longArray) println(longList) // [10, 20, 30] // Преобразование списка обратно в массив val array = Longs.toArray(longList) println(array.contentToString()) // [10, 20, 30] }
import com.google.common.primitives.Doubles fun doublesExample() { val doubleArray = doubleArrayOf(1.1, 2.2, 3.3) // Преобразование массива double в список val doubleList = Doubles.asList(*doubleArray) println(doubleList) // [1.1, 2.2, 3.3] // Преобразование списка обратно в массив val array = Doubles.toArray(doubleList) println(array.contentToString()) // [1.1, 2.2, 3.3] // Сравнение двух double println(Doubles.compare(3.14, 2.71)) // 1 (первый больше второго) }
import com.google.common.primitives.Booleans fun booleansExample() { val booleanArray = booleanArrayOf(true, false, true) // Преобразование массива boolean в список val booleanList = Booleans.asList(*booleanArray) println(booleanList) // [true, false, true] // Преобразование списка обратно в массив val array = Booleans.toArray(booleanList) println(array.contentToString()) // [true, false, true] }
import com.google.common.primitives.Bytes fun bytesExample() { val byteArray = byteArrayOf(1, 2, 3) // Преобразование массива byte в список val byteList = Bytes.asList(*byteArray) println(byteList) // [1, 2, 3] // Преобразование списка обратно в массив val array = Bytes.toArray(byteList) println(array.contentToString()) // [1, 2, 3] }
UnsignedInts, UnsignedLongs:
Поддержка беззнаковых целых чисел.
import com.google.common.primitives.UnsignedInts import com.google.common.primitives.UnsignedLongs fun unsignedIntsExample() { val unsignedInt = UnsignedInts.parseUnsignedInt("4294967295") // Максимальное значение unsigned int println(unsignedInt) // 4294967295 (но хранится как обычный int) val compared = UnsignedInts.compare(4000000000.toInt(), 3000000000.toInt()) println(compared) // 1 (первое больше второго) } fun unsignedLongsExample() { val unsignedLong = UnsignedLongs.parseUnsignedLong("18446744073709551615") // Максимальное значение unsigned long println(unsignedLong) // 18446744073709551615 (но хранится как обычный long) val compared = UnsignedLongs.compare(18000000000000000000L, 17000000000000000000L) println(compared) // 1 (первое больше второго) }
Immutable collections for primitives:
Например, ImmutableIntArray.
import com.google.common.primitives.ImmutableIntArray fun immutableIntArrayExample() { // Создание неизменяемого массива val immutableIntArray = ImmutableIntArray.of(1, 2, 3, 4, 5) println(immutableIntArray) // [1, 2, 3, 4, 5] // Получение элемента println(immutableIntArray.get(2)) // 3 // Попытка изменить массив вызовет ошибку // immutableIntArray.set(0, 10) // UnsupportedOperationException }
Итераторы и генераторы:
Google Guava предоставляет мощные утилиты для работы с итераторами и генераторами данных. Эти утилиты облегчают фильтрацию, преобразование и комбинацию коллекций, предоставляя гибкий и удобный API. Рассмотрим несколько примеров использования итераторов и генераторов данных на Kotlin.
Iterators:
Утилиты для работы с Java Iterator, например, фильтрация, преобразование, объединение и т. д.
Пример фильтрации итератора:
import com.google.common.collect.Iterators fun iteratorFilterExample() { val numbers = listOf(1, 2, 3, 4, 5).iterator() // Фильтруем итератор, оставляя только четные числа val evenNumbers = Iterators.filter(numbers) { it % 2 == 0 } while (evenNumbers.hasNext()) { println(evenNumbers.next()) // 2, 4 } }
Пример преобразования итератора:
import com.google.common.collect.Iterators import com.google.common.base.Function fun iteratorTransformExample() { val numbers = listOf(1, 2, 3, 4, 5).iterator() // Преобразуем итератор, возводя числа в квадрат val squaredNumbers = Iterators.transform(numbers, Function { it * it }) while (squaredNumbers.hasNext()) { println(squaredNumbers.next()) // 1, 4, 9, 16, 25 } }
PeekingIterator:
Итератор, который позволяет «заглянуть» на следующий элемент без его извлечения.
import com.google.common.collect.Iterators import com.google.common.collect.PeekingIterator fun peekingIteratorExample() { val numbers = listOf(1, 2, 3, 4, 5).iterator() val peekingIterator: PeekingIterator<Int> = Iterators.peekingIterator(numbers) while (peekingIterator.hasNext()) { println("Current: ${peekingIterator.next()}") if (peekingIterator.hasNext()) { println("Next (peek): ${peekingIterator.peek()}") } } }
AbstractIterator:
Шаблон для упрощения создания пользовательских итераторов.
import com.google.common.collect.AbstractIterator fun abstractIteratorExample() { val customIterator = object : AbstractIterator<Int>() { var count = 0 override fun computeNext(): Int? { count++ return if (count <= 5) count else endOfData() // Завершаем, когда count > 5 } } while (customIterator.hasNext()) { println(customIterator.next()) // 1, 2, 3, 4, 5 } }
UnmodifiableIterator:
Итератор, который не позволяет изменения. UnmodifiableIterator гарантирует, что элементы не могут быть изменены во время итерации.
import com.google.common.collect.UnmodifiableIterator fun unmodifiableIteratorExample() { val numbers = listOf(1, 2, 3, 4, 5) // Создаем неизменяемый итератор val unmodifiableIterator: UnmodifiableIterator<Int> = object : UnmodifiableIterator<Int>() { private val iter = numbers.iterator() override fun hasNext(): Boolean = iter.hasNext() override fun next(): Int = iter.next() } while (unmodifiableIterator.hasNext()) { println(unmodifiableIterator.next()) // 1, 2, 3, 4, 5 } // unmodifiableIterator.remove() // UnsupportedOperationException }
FluentIterable:
Функциональная работа с коллекциями. Хотя FluentIterable работает как с коллекциями, так и с итераторами, он обеспечивает функциональный стиль работы с ними, объединяя фильтрацию, преобразование и другие операции.
import com.google.common.collect.FluentIterable fun fluentIterableExample() { val numbers = listOf(1, 2, 3, 4, 5) // Комбинируем фильтрацию и преобразование с помощью FluentIterable val result = FluentIterable.from(numbers) .filter { it % 2 == 0 } // Фильтруем четные числа .transform { it * 2 } // Умножаем каждое на 2 .toList() println(result) // [4, 8] }
Преобразования типов:
Google Guava предоставляет мощные утилиты для преобразования типов, такие как TypeToken и Converter. Они позволяют работать с дженериками, преобразовывать объекты между различными типами и управлять безопасностью типов во время выполнения.
TypeToken:
Утилита для работы с типами, поддерживающая дженерики. TypeToken позволяет работать с типами, особенно с дженериками, и извлекать информацию о них во время выполнения. В стандартной Java/Kotlin система типов стирает информацию о дженериках во время выполнения, но с помощью TypeToken можно сохранять информацию о типе.
import com.google.common.reflect.TypeToken fun typeTokenExample() { // Создаем TypeToken для List<String> val typeToken = object : TypeToken<List<String>>() {} // Получаем информацию о типе val type = typeToken.type println(type) // java.util.List<java.lang.String> // Проверяем тип во время выполнения val listOfStrings: Any = listOf("a", "b", "c") if (typeToken.rawType.isAssignableFrom(listOfStrings.javaClass)) { println("listOfStrings является List<String>") } }
Converter:
Абстракция для преобразования одного типа в другой с поддержкой обратной конверсии. Converter позволяет легко преобразовывать объекты из одного типа в другой. Это полезно, когда нужно определить преобразование в обоих направлениях — туда и обратно.
import com.google.common.base.Converter fun converterExample() { // Определяем преобразователь между String и Integer val stringToIntegerConverter = object : Converter<String, Int>() { override fun doForward(string: String): Int { return string.toInt() } override fun doBackward(integer: Int): String { return integer.toString() } } // Преобразуем строку в число val intValue = stringToIntegerConverter.convert("123") println(intValue) // 123 // Преобразуем число обратно в строку val stringValue = stringToIntegerConverter.reverse().convert(456) println(stringValue) // "456" }
Enums:
Преобразование строк в перечисления (и обратно). Guava также упрощает работу с перечислениями (enums), предлагая утилиты для их преобразования из строк и обратно.
import com.google.common.base.Enums enum class Color { RED, GREEN, BLUE } fun enumExample() { // Преобразуем строку в значение перечисления val color = Enums.getIfPresent(Color::class.java, "RED").orNull() println(color) // RED // Получаем строковое представление перечисления val colorName = Enums.stringConverter(Color::class.java).convert(Color.GREEN) println(colorName) // GREEN }
Работа с временем:
Stopwatch:
Таймер для измерения времени выполнения операций.
import com.google.common.base.Stopwatch import java.util.concurrent.TimeUnit fun stopwatchExample() { // Создаем таймер val stopwatch = Stopwatch.createStarted() // Выполняем некоторую операцию Thread.sleep(1000) // Останавливаем таймер stopwatch.stop() // Получаем результат в миллисекундах println("Elapsed time: ${stopwatch.elapsed(TimeUnit.MILLISECONDS)} ms") // Пример: Elapsed time: 1001 ms // Можно перезапустить таймер stopwatch.reset().start() Thread.sleep(500) stopwatch.stop() println("Elapsed time after reset: ${stopwatch.elapsed(TimeUnit.MILLISECONDS)} ms") // Пример: Elapsed time after reset: 500 ms }
Timestamps:
Утилиты для работы с отметками времени. Guava также предлагает утилиты для работы с временными метками и временными интервалами. Однако стандартной утилиты Timestamps в Guava нет. Вместо этого обычно используются классы из пакета java.time (начиная с Java 8). Тем не менее, Stopwatch является основным инструментом Guava для работы с временем.
Пример работы с классами времени из Java 8 (для работы с временными метками):
Хотя это не часть Google Guava, начиная с Java 8, мы можем использовать java.time для работы с временными метками, и это часто используется совместно с утилитами Guava.
import java.time.LocalDateTime import java.time.format.DateTimeFormatter fun timestampExample() { // Получаем текущую временную метку val currentTime = LocalDateTime.now() println("Current timestamp: $currentTime") // Пример: Current timestamp: 2023-10-01T15:45:32.123 // Форматирование временной метки val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") val formattedTime = currentTime.format(formatter) println("Formatted timestamp: $formattedTime") // Пример: Formatted timestamp: 2023-10-01 15:45:32 }
Работа с процессами:
Google Guava предоставляет абстракции для управления сервисами и процессами через пакеты com.google.common.util.concurrent и класс Service. Эти утилиты особенно полезны для управления жизненным циклом процессов или сервисов, которые имеют стадии запуска, выполнения и остановки, что делает их пригодными для долгоживущих процессов, таких как серверы, фоновая обработка задач и другие длительные операции.
Service:
Абстракция для представления сервисов с поддержкой жизненного цикла (инициализация, остановка и т. д.). Предоставляет методы для управления жизненным циклом процессов: запуск, остановка, мониторинг состояния.
AbstractExecutionThreadService:
AbstractExecutionThreadService предоставляет шаблон для создания процессов, которые должны выполняться в отдельных потоках. Этот класс позволяет легко запускать сервис в фоновом потоке и обрабатывать его завершение.
import com.google.common.util.concurrent.AbstractExecutionThreadService fun abstractExecutionThreadServiceExample() { // Создаем сервис, работающий в отдельном потоке val backgroundService: Service = object : AbstractExecutionThreadService() { override fun run() { println("Background service is running") // Эмуляция долгой операции Thread.sleep(5000) println("Background service has completed") } override fun startUp() { println("Background service is starting up") } override fun shutDown() { println("Background service is shutting down") } } // Запускаем сервис backgroundService.startAsync() backgroundService.awaitRunning() // Останавливаем сервис после работы backgroundService.stopAsync() backgroundService.awaitTerminated() }
ServiceManager:
Предоставляет возможность управлять несколькими сервисами одновременно, отслеживать их состояния и корректно завершать их работу.
import com.google.common.util.concurrent.AbstractExecutionThreadService import com.google.common.util.concurrent.Service import com.google.common.util.concurrent.ServiceManager fun serviceManagerExample() { // Создаем два сервиса val service1: Service = object : AbstractExecutionThreadService() { override fun run() { println("Service 1 is running...") Thread.sleep(2000) } override fun shutDown() { println("Service 1 is shutting down...") } } val service2: Service = object : AbstractExecutionThreadService() { override fun run() { println("Service 2 is running...") Thread.sleep(3000) } override fun shutDown() { println("Service 2 is shutting down...") } } // Используем ServiceManager для управления двумя сервисами val serviceManager = ServiceManager(listOf(service1, service2)) // Добавляем обработчик для уведомления о завершении всех сервисов serviceManager.addListener(object : ServiceManager.Listener() { override fun stopped() { println("All services have stopped.") } override fun failure(service: Service) { println("Service failed: ${service.javaClass.name}") } }, Runnable::run) // Запускаем все сервисы serviceManager.startAsync() serviceManager.awaitHealthy() // Ожидание запуска всех сервисов // Останавливаем все сервисы serviceManager.stopAsync() serviceManager.awaitStopped() // Ожидание остановки всех сервисов }
Конкурентное программирование:
ListenableFuture:
Расширение стандартного Future, поддерживающее добавление коллбеков.
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.MoreExecutors import com.google.common.util.concurrent.ListenableFutureTask import java.util.concurrent.Executors fun listenableFutureExample() { // Создаем executor для выполнения задач val executor = Executors.newSingleThreadExecutor() // Создаем ListenableFutureTask для асинхронной операции val futureTask: ListenableFuture<String> = ListenableFutureTask.create<String> { Thread.sleep(2000) // Эмуляция долгой задачи "Task result" } // Добавляем коллбек для обработки результата futureTask.addListener({ try { println("Task completed with result: ${futureTask.get()}") } catch (e: Exception) { println("Error during task execution: ${e.message}") } }, MoreExecutors.directExecutor()) // Запускаем задачу executor.submit(futureTask) }
Futures:
Утилиты для работы с ListenableFuture, включая композицию и комбинирование нескольких будущих значений.
import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFutureTask import java.util.concurrent.Executors fun futuresExample() { // Создаем executor для выполнения задач val executor = Executors.newFixedThreadPool(2) // Создаем две асинхронные задачи val futureTask1 = ListenableFutureTask.create<String> { Thread.sleep(1000) "Result from Task 1" } val futureTask2 = ListenableFutureTask.create<String> { Thread.sleep(2000) "Result from Task 2" } // Запускаем задачи executor.submit(futureTask1) executor.submit(futureTask2) // Комбинируем результаты двух задач val combinedFuture = Futures.allAsList(futureTask1, futureTask2) // Добавляем коллбек для обработки результата combinedFuture.addListener({ try { val results = combinedFuture.get() println("All tasks completed with results: $results") } catch (e: Exception) { println("Error: ${e.message}") } }, MoreExecutors.directExecutor()) }
MoreExecutors:
Расширенные утилиты для работы с экзекьюторами (например, преобразование экзекьютора в ListenableFuture).
import com.google.common.util.concurrent.MoreExecutors import java.util.concurrent.Executors fun moreExecutorsExample() { // Создаем executor с автоматическим завершением работы при завершении всех задач val executor = MoreExecutors.getExitingExecutorService(Executors.newSingleThreadExecutor()) // Запускаем задачу executor.submit { println("Task is running in exiting executor") } // Экзекьютор автоматически завершит свою работу, когда все задачи завершены }
ListeningExecutorService:
Расширяет ExecutorService и поддерживает создание задач, которые возвращают ListenableFuture. Это удобно для управления асинхронными задачами с коллбеками.
import com.google.common.util.concurrent.ListeningExecutorService import com.google.common.util.concurrent.MoreExecutors import java.util.concurrent.Executors fun listeningExecutorServiceExample() { // Создаем ListeningExecutorService val executorService: ListeningExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(2)) // Запускаем асинхронную задачу val future = executorService.submit<String> { Thread.sleep(1000) "Async result" } // Добавляем коллбек для обработки результата future.addListener({ try { println("Task completed with result: ${future.get()}") } catch (e: Exception) { println("Error: ${e.message}") } }, MoreExecutors.directExecutor()) }
RateLimiter:
Используется для ограничения скорости выполнения операций, например, для контроля количества запросов в секунду.
import com.google.common.util.concurrent.RateLimiter fun rateLimiterExample() { // Создаем RateLimiter с лимитом в 2 операции в секунду val rateLimiter = RateLimiter.create(2.0) // Выполняем операции с ограничением по скорости for (i in 1..5) { rateLimiter.acquire() // Блокируем выполнение, пока лимит не позволит продолжить println("Executing task $i at ${System.currentTimeMillis()}") } }
Утилиты для работы с IO:
Files:
Утилиты для работы с файлами (копирование, перемещение, чтение, запись и т. д.).
Пример чтения и записи файлов:
import com.google.common.io.Files import java.io.File import java.nio.charset.StandardCharsets fun filesExample() { val file = File("example.txt") // Записываем данные в файл Files.asCharSink(file, StandardCharsets.UTF_8).write("Hello, world!") // Читаем данные из файла val content = Files.asCharSource(file, StandardCharsets.UTF_8).read() println("File content: $content") // Hello, world! }
Пример копирования файла:
import com.google.common.io.Files import java.io.File fun fileCopyExample() { val sourceFile = File("source.txt") val targetFile = File("target.txt") // Копируем файл Files.copy(sourceFile, targetFile) println("File copied from ${sourceFile.name} to ${targetFile.name}") }
Пример перемещения файла:
import com.google.common.io.Files import java.io.File fun fileMoveExample() { val sourceFile = File("source.txt") val targetFile = File("moved.txt") // Перемещаем файл Files.move(sourceFile, targetFile) println("File moved from ${sourceFile.name} to ${targetFile.name}") }
CharStreams:
Утилиты для работы с потоками символов.
Пример чтения данных из Reader:
import com.google.common.io.CharStreams import java.io.StringReader fun charStreamsExample() { val reader = StringReader("Hello from StringReader!") // Читаем все данные из Reader val content = CharStreams.toString(reader) println(content) // Hello from StringReader! }
Пример записи данных в Writer:
import com.google.common.io.CharStreams import java.io.StringWriter fun charStreamsWriteExample() { val writer = StringWriter() // Записываем данные в Writer CharStreams.asWriter(writer).write("Hello to StringWriter!") println(writer.toString()) // Hello to StringWriter! }
ByteStreams:
Утилиты для работы с байтовыми потоками.
Пример копирования данных из одного потока в другой:
import com.google.common.io.ByteStreams import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream fun byteStreamsExample() { val input = ByteArrayInputStream("Hello in bytes!".toByteArray()) val output = ByteArrayOutputStream() // Копируем данные из InputStream в OutputStream ByteStreams.copy(input, output) println(output.toString()) // Hello in bytes! }
Пример чтения всех данных из InputStream:
import com.google.common.io.ByteStreams import java.io.ByteArrayInputStream fun byteStreamsReadExample() { val input = ByteArrayInputStream("Some byte data".toByteArray()) // Читаем все данные из InputStream val byteData = ByteStreams.toByteArray(input) println(String(byteData)) // Some byte data }
Closer:
Упрощает управление ресурсами, такими как файлы и потоки, обеспечивая их автоматическое закрытие в случае ошибки или успешного завершения работы.
import com.google.common.io.Closer import java.io.FileReader import java.io.FileWriter fun closerExample() { val closer = Closer.create() try { // Открываем ресурсы val reader = closer.register(FileReader("input.txt")) val writer = closer.register(FileWriter("output.txt")) // Читаем данные и записываем их val data = reader.readText() writer.write(data) } catch (e: Exception) { println("Error occurred: ${e.message}") throw e } finally { closer.close() // Автоматически закрывает все ресурсы } }
Resources:
Guava также предоставляет утилиты для работы с ресурсами в виде файлов, которые находятся в classpath.
import com.google.common.io.Resources import java.nio.charset.StandardCharsets fun resourcesExample() { // Читаем ресурс из classpath val url = Resources.getResource("config.txt") val content = Resources.toString(url, StandardCharsets.UTF_8) println(content) }
Утилиты для работы с потоками (Streams):
Stream:
Расширенные утилиты для работы с потоками данных.
import com.google.common.collect.Streams fun streamsExample() { val list1 = listOf(1, 2, 3) val list2 = listOf(4, 5, 6) // Объединяем два списка в один поток val combinedStream = Streams.concat(list1.stream(), list2.stream()) // Преобразуем поток в список val combinedList = combinedStream.toList() println(combinedList) // [1, 2, 3, 4, 5, 6] }
Утилита Streams.zip позволяет объединить два потока в один с помощью функции:
import com.google.common.collect.Streams import java.util.stream.Stream fun streamsZipExample() { val stream1 = Stream.of(1, 2, 3) val stream2 = Stream.of("a", "b", "c") // Объединяем два потока, создавая пары (число, строка) val zippedStream = Streams.zip(stream1, stream2) { number, letter -> "$number$letter" } // Преобразуем поток в список val result = zippedStream.toList() println(result) // [1a, 2b, 3c] }
Hashing (Хеширование):
Hashing:
Поддержка различных хеш-функций (MD5, SHA-256, MurmurHash и др.).
import com.google.common.hash.Hashing import java.nio.charset.StandardCharsets fun hashingExample() { val input = "Hello, World!" // Используем SHA-256 для хеширования строки val hash = Hashing.sha256() .hashString(input, StandardCharsets.UTF_8) .toString() println("SHA-256 hash: $hash") // Пример вывода: SHA-256 hash: c0535e4be2b79ffd93291305436bf889314e4a3d9a91fb217b0b14cba2999e66 }
import com.google.common.hash.Hashing import java.nio.charset.StandardCharsets fun murmurHashExample() { val input = "Guava Hashing Example" // Используем MurmurHash3 для хеширования строки val hash = Hashing.murmur3_128() .hashString(input, StandardCharsets.UTF_8) .toString() println("MurmurHash3 (128-bit) hash: $hash") // Пример вывода: MurmurHash3 (128-bit) hash: 2f17b911e9d9b22fa10f0ab091d02e17 }
Hasher:
Позволяет поэтапно добавлять данные для хеширования. Это полезно, если нужно хешировать данные, поступающие из нескольких источников, или данные, которые необходимо хешировать последовательно.
import com.google.common.hash.Hashing import java.nio.charset.StandardCharsets fun hasherExample() { val hasher = Hashing.sha256().newHasher() // Добавляем данные для хеширования по частям hasher.putString("Hello", StandardCharsets.UTF_8) hasher.putInt(12345) hasher.putLong(9876543210L) // Получаем итоговый хеш val hash = hasher.hash().toString() println("Combined SHA-256 hash: $hash") // Пример вывода: Combined SHA-256 hash: 3627904d36e33402e1dba8e132ea9d222f3baaf3dcb9bfb144f50c3cf555160b }
HashCode:
Представляет результат хеширования, который можно преобразовать в строку, массив байтов или использовать для сравнения.
import com.google.common.hash.Hashing import java.nio.charset.StandardCharsets fun hashCodeExample() { val hashCode = Hashing.sha256() .hashString("Guava HashCode Example", StandardCharsets.UTF_8) // Преобразуем хеш в строку println("Hash as string: ${hashCode.toString()}") // Преобразуем хеш в байтовый массив val byteArray = hashCode.asBytes() println("Hash as byte array: ${byteArray.contentToString()}") }
BloomFilter:
вероятностная структура данных, используемая для проверки принадлежности элемента к множеству с небольшой вероятностью ложных срабатываний. Этот инструмент полезен, когда требуется экономить память и можно допустить небольшую вероятность ложных срабатываний.
import com.google.common.hash.BloomFilter import com.google.common.hash.Funnels fun bloomFilterExample() { // Создаем фильтр Блума для строк с ожидаемой пропускной способностью 1000 и вероятностью ложных срабатываний 0.01 val bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8), 1000, 0.01) // Добавляем данные в фильтр bloomFilter.put("Guava") bloomFilter.put("BloomFilter") bloomFilter.put("Hashing") // Проверяем наличие элементов println("Might contain 'Guava': ${bloomFilter.mightContain("Guava")}") // true println("Might contain 'Hashing': ${bloomFilter.mightContain("Hashing")}") // true println("Might contain 'Unknown': ${bloomFilter.mightContain("Unknown")}") // false (вероятность ложного срабатывания 0.01) }
Custom Hash Functions:
Создание собственной хеш-функции. Если встроенные алгоритмы хеширования не подходят, можно создать собственную хеш-функцию, реализуя интерфейс HashFunction.
import com.google.common.hash.AbstractHasher import com.google.common.hash.HashFunction import com.google.common.hash.HashCode fun customHashFunctionExample() { val customHashFunction = object : HashFunction { override fun newHasher() = object : AbstractHasher() { private var sum = 0 override fun putInt(i: Int): AbstractHasher { sum += i return this } override fun putString(input: CharSequence, charset: java.nio.charset.Charset): AbstractHasher { sum += input.length return this } override fun hash(): HashCode { return HashCode.fromInt(sum) } } override fun bits(): Int = 32 } val hasher = customHashFunction.newHasher() hasher.putInt(123) hasher.putString("custom hash function", StandardCharsets.UTF_8) val hash = hasher.hash() println("Custom hash code: ${hash.asInt()}") // Пример вывода: Custom hash code: 143 }
Математические утилиты:
Ints, Longs, Doubles, BigIntegerMath, DoubleMath:
Утилиты для математических операций с числами различных типов.
RoundingMode:
Утилиты для округления чисел.
Работа с аннотациями:
@Beta:
Пометка о нестабильных API. Указывает, что API может измениться в будущем. Аннотация @Beta используется для пометки классов, методов и API, которые могут измениться в будущих версиях. Этот API не считается стабильным, и его использование в продакшене рекомендуется с осторожностью, так как он может измениться без обратной совместимости.
import com.google.common.annotations.Beta @Beta class BetaFeature { fun newFeature() { println("This feature is in beta and may change!") } } fun betaFeatureExample() { val feature = BetaFeature() feature.newFeature() }
@VisibleForTesting:
Доступность для тестирования. Указывает, что метод или класс предназначен для использования в тестах. Аннотация @VisibleForTesting используется для того, чтобы показать, что определенный метод или поле доступны только для тестов. Это может быть полезно для обозначения методов, которые не предназначены для использования вне тестов, но являются открытыми для тестирования.
import com.google.common.annotations.VisibleForTesting class ProductionClass { // Этот метод доступен только для тестов @VisibleForTesting internal fun internalLogicForTests(): Int { return 42 } fun publicLogic(): String { return "Public logic result" } } fun testVisibilityExample() { val productionClass = ProductionClass() // Можно вызывать метод в тестах val testResult = productionClass.internalLogicForTests() println("Test logic result: $testResult") // Test logic result: 42 }
@Nullable:
Аннотация для указания возможности null. Аннотация @Nullable указывает, что метод может возвращать null или что параметр метода может принимать значение null. Это помогает повысить безопасность и прозрачность кода, показывая, где может возникнуть null.
import javax.annotation.Nullable class NullableExample { // Метод может вернуть null @Nullable fun findValue(key: String): String? { return if (key == "exists") "Found value" else null } } fun nullableExample() { val example = NullableExample() val value = example.findValue("not_exists") println("Value: $value") // Value: null }
@CheckReturnValue:
Обязательная проверка возвращаемого значения. Аннотация @CheckReturnValue указывает, что возвращаемое значение метода должно быть проверено, и его игнорирование может привести к ошибке. Эта аннотация полезна для методов, результат которых важно обработать.
import com.google.errorprone.annotations.CheckReturnValue class CheckReturnValueExample { @CheckReturnValue fun computeValue(): Int { return 42 } } fun checkReturnValueExample() { val example = CheckReturnValueExample() // Предполагается, что это значение должно быть использовано val result = example.computeValue() println("Computed value: $result") // Computed value: 42 }
@CanIgnoreReturnValue:
Разрешение игнорирования возвращаемого значения. Аннотация @CanIgnoreReturnValue позволяет игнорировать возвращаемое значение метода. Она полезна в ситуациях, когда возвращаемое значение необязательно должно использоваться.
import com.google.errorprone.annotations.CanIgnoreReturnValue class IgnoreReturnValueExample { @CanIgnoreReturnValue fun performAction(): String { return "Action performed" } } fun ignoreReturnValueExample() { val example = IgnoreReturnValueExample() // Возвращаемое значение можно игнорировать example.performAction() // Значение не обязательно использовать }
Прочие утилиты:
Preconditions:
Утилиты для проверки условий, используемые для валидации аргументов методов. Google Guava предоставляет утилиту Preconditions, которая помогает выполнять проверки аргументов методов и состояний программы. Эти проверки повышают читаемость и надежность кода, поскольку позволяют выбрасывать исключения с информативными сообщениями в случае некорректных данных.
Утилита Preconditions предоставляет следующие основные методы:
checkArgument:
используется для проверки условий, касающихся переданных аргументов. Если условие не выполняется, выбрасывается IllegalArgumentException.
import com.google.common.base.Preconditions fun checkArgumentExample(age: Int) { // Проверка, что возраст больше или равен 18 Preconditions.checkArgument(age >= 18, "Age must be 18 or older, but was %s", age) println("Valid age: $age") } fun main() { checkArgumentExample(20) // Valid age: 20 // checkArgumentExample(15) // IllegalArgumentException: Age must be 18 or older, but was 15 }
checkNotNull:
проверяет, что переданный аргумент не равен null. В случае, если аргумент null, выбрасывается NullPointerException.
import com.google.common.base.Preconditions fun checkNotNullExample(name: String?) { // Проверка, что имя не равно null val validName = Preconditions.checkNotNull(name, "Name cannot be null") println("Valid name: $validName") } fun main() { checkNotNullExample("Alice") // Valid name: Alice // checkNotNullExample(null) // NullPointerException: Name cannot be null }
checkState:
используется для проверки состояния объекта. Если условие не выполняется, выбрасывается IllegalStateException.
import com.google.common.base.Preconditions class StateExample(private var isInitialized: Boolean) { fun doSomething() { // Проверка, что объект инициализирован Preconditions.checkState(isInitialized, "Object must be initialized before use") println("Doing something") } fun initialize() { isInitialized = true } } fun main() { val example = StateExample(false) // example.doSomething() // IllegalStateException: Object must be initialized before use example.initialize() example.doSomething() // Doing something }
checkElementIndex:
проверяет, что индекс элемента находится в пределах допустимого диапазона (от 0 до размера списка/массива). Если индекс выходит за пределы, выбрасывается IndexOutOfBoundsException.
import com.google.common.base.Preconditions fun checkElementIndexExample(index: Int, list: List<String>) { // Проверка, что индекс находится в пределах списка Preconditions.checkElementIndex(index, list.size, "Invalid index") println("Element at index $index: ${list[index]}") } fun main() { val list = listOf("apple", "banana", "cherry") checkElementIndexExample(1, list) // Element at index 1: banana // checkElementIndexExample(5, list) // IndexOutOfBoundsException: Invalid index (5) must be less than size (3) }
checkPositionIndex:
проверяет позицию в коллекции или массиве. Позиция — это индекс, который может указывать на начало или конец коллекции. Если позиция невалидна, выбрасывается IndexOutOfBoundsException.
import com.google.common.base.Preconditions fun checkPositionIndexExample(position: Int, list: List<String>) { // Проверка позиции Preconditions.checkPositionIndex(position, list.size, "Invalid position") println("Valid position: $position") } fun main() { val list = listOf("apple", "banana", "cherry") checkPositionIndexExample(3, list) // Valid position: 3 (указание на конец списка) // checkPositionIndexExample(5, list) // IndexOutOfBoundsException: Invalid position (5) must be less than size (3) }
checkPositionIndexes:
проверяет, что начальный и конечный индексы находятся в допустимом диапазоне и что начальный индекс не больше конечного.
import com.google.common.base.Preconditions fun checkPositionIndexesExample(start: Int, end: Int, list: List<String>) { // Проверка диапазона индексов Preconditions.checkPositionIndexes(start, end, list.size) println("Valid range: from $start to $end") } fun main() { val list = listOf("apple", "banana", "cherry", "date") checkPositionIndexesExample(0, 2, list) // Valid range: from 0 to 2 // checkPositionIndexesExample(2, 5, list) // IndexOutOfBoundsException: End index (5) must not be greater than size (4) }
Objects:
Утилиты для работы с объектами (сравнение, генерация hashCode и т. д.).
MoreObjects:
Дополнительные утилиты для работы с объектами, включая toStringHelper.
ComparisonChain:
Утилита для создания цепочек сравнений объектов, которая упрощает реализацию методов compareTo.