Iterable<T> collection = ...
for (T e: collection) {
...
}
@inponomarev
Иван Пономарёв, КУРС/МФТИ
|
|
public interface Comparable<T>{
/**
* @param o the object to be compared.
* @return a negative integer, zero, or a positive integer as this object
* is less than, equal to, or greater than the specified object.
*/
int compareTo(T o);
}
/*Применяется в случае, если сравниваемые объекты
не реализуют Comparable*/
public interface Comparator<T> {
int compare(T o1, T o2);
}
Используйте интерфейсы для типов переменных и аргументов в методах. Не привязывайтесь к конкретным реализациям.
Помимо возможности подмены реализаций, существуют суррогатные коллекции, например, важные частные случаи:
//ИММУТАБЕЛЬНЫЕ
//Пустые //Из одного элемента
Collections.emptyList(); Collections.singletonList(o);
Collections.emptySet(); Collections.singleton(o);
Collections.emptyMap(); Collections.singletonMap(k,v);
//Из заданных элементов
List.of(a, b, c...);
Set.of(a, b, c...);
Map.of(k1, v1, k2, v2...);
Но как они это сделали для мапы?
Collections.unmodifiableList(List<? extends T> l);
Collections.unmodifiableSet(Set<? extends T> s);
Collections.unmodifiableMap(Map<? extends K,
? extends V> s);
...
В классе Collections
(как и в классе Arrays
) вообще очень много всего полезного!
List<String> list = new ArrayList<>();
list.add("foo"); list.add("bar"); list.add("baz");
(Визуализация внутреннего устройства структур данных сделана с помощью Lightweight Java Visualizer.)
ArrayList
get(int index)
is O(1) ← main benefit of ArrayList<E>
add(E element)
is O(1) amortized, but O(n) worst-case since the array must be resized and copied
add(int index, E element)
is O(n) (with n/2 steps on average)
remove(int index)
is O(n) (with n/2 steps on average)
Iterator.remove()
is O(n) (with n/2 steps on average)
ListIterator.add(E element)
is O(n) (with n/2 steps on average)
List<String> list = new LinkedList<>();
list.add("foo"); list.add("bar"); list.add("baz");
LinkedList
Implements List
and Deque
get(int index)
is O(n) (with n/4 steps on average)
add(E element)
is O(1)
add(int index, E element)
is O(n) (with n/4 steps on average),
but O(1) when index = 0
← main benefit of LinkedList<E>
remove(int index)
is O(n) (with n/4 steps on average)
Iterator.remove()
is O(1) ← main benefit of LinkedList<E>
ListIterator.add(E element)
is O(1) This is one of the main benefits of LinkedList<E>
ArrayDeque
Circular array
Более быстрый, чем LinkedList
.
Постановка в очередь с сортировкой по приоритету за счёт Comparable
или Comparator
.
Balanced binary heap: "the two children of queue[n]
are queue[2*n+1]
and queue[2*n+2]
"
PriorityQueue<String> q = new PriorityQueue<>();
q.add("foo"); q.add("bar"); q.add("baz");
Теоретическая асимптотика не полностью описывает пригодность той или иной структуры данных: вмешивается ряд обстоятельств, таких как
частные «удачные» случаи и частота их использования,
эффективность использования кэша,
количество производимого «мусора»
и т. п.
ArrayList
и ArrayDeque
являются предпочтительным выбором реализации List
и Deque
в подавляющем большинстве случаев.
Map<String, Integer> map = new HashMap<>();
map.put("foo", 1); map.put("bar", 2); map.put("baz", 3);
|
|
IdentityHashMap
— ключи сравниваются по ==
, а не по equals()
.
WeakHashMap
— значения могут быть собраны сборщиком мусора, если не используются где-то ещё.
|
Коллекции, в которых объект может присутствовать только один раз.
Реализованы на базе соответствующих Maps:
HashSet
,
LinkedHashSet
,
TreeSet
.
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
EnumSet
— на базе единственного значения типа long
. Используйте только его для enum-ов!
BitSet
— массив long-ов.
|
|
|
|
поиск min/max
копирование
разворачивание «задом наперёд»
объединение и разность
… — ищите и найдёте!
public interface ActionListener {
void actionPerformed(ActionEvent event);
}
// ----использование----
String text = ...
new Timer(1000, new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
... //'text' var is available here
}
});
String[] friends = {"Peter", "Paul", "Mary"};
Arrays.sort(friends,
new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
File[] hiddenFiles = new File(".").listFiles(
new FileFilter() {
public boolean accept(File file) {
return file.isHidden();
}
}
);
//single-line
(String first, String second) ->
first.length() - second.length()
//multi-line with int return
(String first, String second) -> {
if (first.length() < second.length()) return -1;
else if (first.length() > second.length()) return 1;
else return 0;
}
//no-arg, void return
() -> System.out.println("Hello!");
() → {}
() → "Raoul"
() → {return "Mario";}
(Integer i) → return "Alan" + i
(String s) → {"Iron Man"}
Ответ: 1-3. Если лямбда с фигурными скобками, обязательно нужен return. Если без них, то return не нужен!
Функциональный интерфейс — такой, у которого не более одного абстрактного метода (понятно, что запускать).
Может быть помечен аннотацией @FunctionalInterface
, хотя это не обязательно.
Если метод интерфейса подходит по параметрам и возвращаемому значению лямбды — welcome.
//Типы аргументов лямбд указывать не надо: type inference!
ActionListener = e -> {...}
Comparator<String> c = (s1, s2) -> s1.length() - s2.length();
Оба варианта скомпилируются:
final List<String> list = ...
//Predicate.test возвращает boolean
Predicate<String> p = s -> list.add(s);
//Consumer.accept возвращает void!
Consumer<String> c = s -> list.add(s);
//не скомпилируется
Object o = ()->{};
//Скомпилируется!
Runnable r = ()->{};
//Тоже скомпилируется
Object o = (Runnable) ()->{};
Anonymous Class | Lambda |
|
|
|
|
void repeatMessage(String text, int delay) {
ActionListener listener = event -> {
//переменная text доступна внутри лямбды!
System.out.println(text);
}
new Timer(delay, listener).start();
}
«Ингредиенты» лямбды:
Код
Параметры
Свободные («захваченные») переменные, которые должны быть effectively final
int start = ...
for (int i = 0; i < count; i++) {
ActionListener linstener = event -> {
start--; //ERROR: Can't mutate captured value
//ERROR: Cannot refer to changing i
System.out.println(i);
}
}
(Effectively final переменные — это такие, что они либо уже final
, либо, если на них проставить final
, код всё равно будет компилироваться)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
obj = null;
//NPE только в момент запуска лямбды!!
//obj должен быть effectively final!
doSmth(x -> obj.method(x));
//NPE здесь и сейчас
//obj не обязан быть effectively final
doSmth(obj::method)
//Да ладно, разве трудно на null проверить??
Objects.isNull(Object obj)...
Objects.nonNull(Object ob)...
list.removeIf(Objects::isNull);
stream.filter(Objects::nonNull)...
Functional Interface | Parameter Types | Return Type | Abstract Method Name | Default Methods |
---|---|---|---|---|
Runnable | none | void | run | |
Supplier<T> | none | T | get | |
Consumer<T> | T | void | accept | andThen |
BiConsumer<T, U> | T, U | void | accept | andThen |
Consumer<String> foo = ...
List<String> list = ...
//Составной консьюмер, отправляющий
//объект сначала в первый, потом во второй
Consumer<String> bar = foo.andThen(list::add);
Functional Interface | Parameter Types | Return Type | Abstract Method Name | Default Methods |
---|---|---|---|---|
Function<T, R> | T | R | apply | compose, andThen, identity |
BiFunction<T, U, R> | T, U | R | apply | andThen |
|
|
|
|
|
|
Почему у BiFunction
нет compose
, только andThen
?
Functional Interface | Parameter Types | Return Type | Abstract Method Name | Default Methods |
---|---|---|---|---|
UnaryOperator<T> | T | T | apply | compose, andThen, identity |
BinaryOperator<T> | T, T | T | apply | andThen, maxBy, minBy |
UnaryOperator<T> extends Function<T,T>
, поэтому compose
, andThen
и identity
работают так же.
BinaryOperator<T> extends BiFunction<T,T,T>
, поэтому andThen
работает так же
Статические методы minBy
и maxBy
формируют операторы min(x,y)
и max(x,y)
из компаратора.
Functional Interface | Parameter Types | Return Type | Abstract Method Name | Default Methods |
---|---|---|---|---|
Predicate<T> | T | boolean | test | and, or, negate, isEqual |
BiPredicate<T, U> | T, U | boolean | test | and, or, negate |
Predicate<T> a = ...
Predicate<T> b = ...
a.and(b).negate(); // ! (a(x) & b(x)
//Двуместный предикат (x,y)-> Objects.equals(x, y)
Objects::equals
//Одноместный предикат y -> Objects.equals(x, y)
Predicate.isEqual(x)
p, q is int
, long
, double
; P, Q is Int
, Long
, Double
Functional Interface | Parameter Types | Return Type | Abstract Method Name |
---|---|---|---|
BooleanSupplier | none | boolean | getAsBoolean |
PSupplier | none | p | getAsP |
PConsumer | p | void | accept |
ObjPConsumer<T> | T, p | void | accept |
PFunction<T> | p | T | apply |
PToQFunction | p | q | applyAsQ |
ToPFunction<T> | T | p | applyAsP |
ToPBiFunction<T, U> | T, U | p | applyAsP |
Functional Interface | Parameter Types | Return Type | Abstract Method Name |
---|---|---|---|
PUnaryOperator | p | p | applyAsP |
PBinaryOperator | p, p | p | applyAsP |
PPredicate | p | boolean | test |
Работают атомарно в ConcurrentHashMap
!
V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction)
V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction)
V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction)
V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction)
@AllArgsConstructor
public class Person {
@Getter
private final String firstName;
@Getter
private final String lastName;
}
List<Person> people = new ArrayList<>();
|
|
//НЕ ПИШИТЕ ВЕСЬ ЭТОТ КОШМАР
Collections.sort(people,
(p1, p2) -> {
int result = p1.getFirstName().compareTo(p2.getFirstName());
if (result == 0) {
result = p2.getLastName().compareTo(p2.getLastName());
}
return result;
}
);
Collections.sort(people,
Comparator
.comparing(Person::getLastName)
.thenComparing(Person::getFirstName));
);
Comparator.comparing(keyExtractor, keyComparator)
Comparator.comparingInt/Double(...)
Comparator.reversed()
Comparator.nullsFirst/nullsLast(Comparator c)