1 package fr.univtln.bruno.samples.java101.tp3.bestpractices;
2
3 import lombok.extern.slf4j.Slf4j;
4
5 import java.util.*;
6
7
8
9
10 @Slf4j
11 public class CollectionBestPractices {
12
13
14
15
16 private CollectionBestPractices() {
17 }
18
19
20
21
22 public static void defensiveCopyingExample() {
23 log.info("=== Defensive Copying Example ===");
24
25
26 class BadExample {
27 private final List<String> items = new ArrayList<>();
28
29 public List<String> getItems() {
30 return items;
31 }
32 }
33
34 BadExample bad = new BadExample();
35 bad.getItems().add("Item 1");
36 List<String> exposed = bad.getItems();
37 exposed.add("Item 2");
38 log.debug("BadExample internal items: {}", bad.getItems());
39 log.info("Bad example - items modified externally: {}", bad.getItems());
40
41
42 class GoodExample {
43 private final List<String> items = new ArrayList<>();
44
45 public List<String> getItems() {
46 return List.copyOf(items);
47 }
48
49 public void addItem(String item) {
50 items.add(item);
51 }
52 }
53
54 GoodExample good = new GoodExample();
55 good.addItem("Item 1");
56 List<String> copy = good.getItems();
57 try {
58 copy.add("Item 2");
59 } catch (UnsupportedOperationException e) {
60 log.debug("GoodExample returned list (immutable) snapshot: {}", copy);
61 log.info("Good example - cannot modify returned list");
62 }
63 }
64
65
66
67
68 public static void choosingCollectionTypeExample() {
69 log.info("=== Choosing Collection Type Example ===");
70 List<String> arrayList = new ArrayList<>();
71 arrayList.add("A");
72 arrayList.add("B");
73 arrayList.add("C");
74 log.info("ArrayList access index 1: {}", arrayList.get(1));
75
76 LinkedList<String> linkedList = new LinkedList<>();
77 linkedList.addFirst("First");
78 linkedList.addLast("Last");
79 log.info("LinkedList: {}", linkedList);
80
81 Set<String> hashSet = new HashSet<>();
82 hashSet.add("A");
83 hashSet.add("A");
84 log.info("HashSet uniqueness: {}", hashSet);
85
86 TreeSet<Integer> treeSet = new TreeSet<>(Set.of(5, 1, 3, 2, 4));
87 log.info("TreeSet sorted: {}", treeSet);
88 log.info("TreeSet headSet(<4): {}", treeSet.headSet(4));
89
90 Map<String, Integer> hashMap = new HashMap<>();
91 hashMap.put("Alice", 30);
92 log.info("HashMap lookup Alice: {}", hashMap.get("Alice"));
93 }
94
95
96
97
98 public static void immutabilityExample() {
99 log.info("=== Immutability Example ===");
100 List<String> immutableList = List.of("A", "B", "C");
101 Set<String> immutableSet = Set.of("X", "Y", "Z");
102 Map<String, Integer> immutableMap = Map.of("A", 1, "B", 2);
103 log.info("Immutable list: {}", immutableList);
104 log.info("Immutable set: {}", immutableSet);
105 log.info("Immutable map: {}", immutableMap);
106 try {
107 immutableList.add("D");
108 } catch (UnsupportedOperationException e) {
109 log.info("Cannot modify immutable list");
110 }
111
112 List<String> mutable = new ArrayList<>(List.of("A", "B", "C"));
113 List<String> copy = List.copyOf(mutable);
114 mutable.add("D");
115 log.info("Mutable after add: {}", mutable);
116 log.info("Immutable copy unchanged: {}", copy);
117 }
118
119
120
121
122 public static void initializationExample() {
123 log.info("=== Initialization Example ===");
124 List<String> emptyList = Collections.emptyList();
125 Set<String> emptySet = Collections.emptySet();
126 Map<String, Integer> emptyMap = Collections.emptyMap();
127 log.info("Empty collections ready");
128
129 List<String> singletonList = Collections.singletonList("Only");
130 Set<String> singletonSet = Collections.singleton("Only");
131 Map<String, Integer> singletonMap = Collections.singletonMap("Key", 1);
132 log.info("Singletons: {}, {}, {}", singletonList, singletonSet, singletonMap);
133
134 List<String> modernList = List.of("A", "B", "C");
135 Set<String> modernSet = Set.of("X", "Y", "Z");
136 Map<String, Integer> modernMap = Map.of("A", 1, "B", 2);
137 log.info("Modern init: {}, {}, {}", modernList, modernSet, modernMap);
138
139 List<String> withCapacity = new ArrayList<>(100);
140 Map<String, Integer> mapWithCapacity = new HashMap<>(100);
141 log.info("Pre-sized collections created (100)");
142 }
143
144
145
146
147 public static void nullHandlingExample() {
148 log.info("=== Null Handling Example ===");
149 try {
150 List.of("A", null, "C");
151 } catch (NullPointerException e) {
152 log.info("List.of() rejects nulls");
153 }
154 List<String> arrayList = new ArrayList<>();
155 arrayList.add("A");
156 arrayList.add(null);
157 arrayList.add("C");
158 log.info("ArrayList allows nulls: {}", arrayList);
159 Map<String, String> hashMap = new HashMap<>();
160 hashMap.put("key1", "value1");
161 hashMap.put(null, "nullKey");
162 hashMap.put("key2", null);
163 log.info("HashMap with nulls: {}", hashMap);
164 try {
165 Set<String> ts = new TreeSet<>();
166 ts.add(null);
167 } catch (NullPointerException e) {
168 log.info("TreeSet rejects nulls");
169 }
170 Map<String, Integer> scores = new HashMap<>();
171 scores.put("Alice", 100);
172 int bobScore = scores.getOrDefault("Bob", 0);
173 log.info("Default for Bob: {}", bobScore);
174 }
175
176
177
178
179 public static void iterationExample() {
180 log.info("=== Iteration Best Practices ===");
181 List<String> items = new ArrayList<>(List.of("A", "B", "C", "D"));
182 try {
183 for (String item : items) {
184 if (item.equals("B")) items.remove(item);
185 }
186 } catch (ConcurrentModificationException e) {
187 log.info("Foreach removal triggers CME");
188 }
189
190 items = new ArrayList<>(List.of("A", "B", "C", "D"));
191 Iterator<String> it = items.iterator();
192 while (it.hasNext()) {
193 if (it.next().equals("B")) it.remove();
194 }
195 log.info("After iterator removal: {}", items);
196
197 items = new ArrayList<>(List.of("A", "B", "C", "D"));
198 items.removeIf("B"::equals);
199 log.info("After removeIf: {}", items);
200
201 items = new ArrayList<>(List.of("A", "B", "C", "D"));
202 List<String> filtered = items.stream().filter(i -> !i.equals("B")).toList();
203 log.info("Stream filtered: {}", filtered);
204 }
205
206
207
208
209 public static void performanceExample() {
210 log.info("=== Performance Considerations ===");
211 int size = 10_000;
212 List<Integer> arrayList = new ArrayList<>();
213 for (int i = 0; i < size; i++) arrayList.add(i);
214 long start = System.nanoTime();
215 long sum = 0;
216 for (int i = 0; i < size; i++) sum += arrayList.get(i);
217 long arrayNs = System.nanoTime() - start;
218
219 List<Integer> linkedList = new LinkedList<>();
220 for (int i = 0; i < size; i++) linkedList.add(i);
221 start = System.nanoTime();
222 sum = 0;
223 for (int i = 0; i < size; i++) sum += linkedList.get(i);
224 long linkedNs = System.nanoTime() - start;
225 log.info("Random access ArrayList={}ns LinkedList={}ns", arrayNs, linkedNs);
226
227 Set<Integer> hashSet = new HashSet<>();
228 for (int i = 0; i < size; i++) hashSet.add(i);
229 start = System.nanoTime();
230 for (int i = 0; i < size; i++) hashSet.contains(i);
231 long hashNs = System.nanoTime() - start;
232 Set<Integer> treeSet = new TreeSet<>();
233 for (int i = 0; i < size; i++) treeSet.add(i);
234 start = System.nanoTime();
235 for (int i = 0; i < size; i++) treeSet.contains(i);
236 long treeNs = System.nanoTime() - start;
237 log.debug("Benchmark timings ns arrayList={} linkedList={} hashSet={} treeSet={}", arrayNs, linkedNs, hashNs, treeNs);
238 log.info("Contains HashSet={}ns TreeSet={}ns", hashNs, treeNs);
239 }
240
241
242
243
244 public static void commonPitfallsExample() {
245 log.info("=== Common Pitfalls Example ===");
246 List<Integer> list1 = Arrays.asList(1, 2, 3);
247 List<Integer> list2 = Arrays.asList(1, 2, 3);
248 log.info("list1 == list2: {}", list1 == list2);
249 log.info("list1.equals(list2): {}", list1.equals(list2));
250
251 class BadPerson {
252 final String name;
253
254 BadPerson(String n) {
255 name = n;
256 }
257 }
258 Set<BadPerson> badSet = new HashSet<>();
259 badSet.add(new BadPerson("Alice"));
260 badSet.add(new BadPerson("Alice"));
261 log.info("BadPerson set size (expected 1 but got {}): {}", badSet.size(), badSet.size());
262
263 class MutableKey {
264 int value;
265
266 MutableKey(int v) {
267 value = v;
268 }
269
270 @Override
271 public int hashCode() {
272 return value;
273 }
274
275 @Override
276 public boolean equals(Object o) {
277 return o instanceof MutableKey mk && mk.value == value;
278 }
279 }
280 Map<MutableKey, String> map = new HashMap<>();
281 MutableKey key = new MutableKey(1);
282 map.put(key, "Value");
283 log.info("Contains before mutation: {}", map.containsKey(key));
284 key.value = 2;
285 log.info("Contains after mutation: {}", map.containsKey(key));
286
287 List<String> fixed = Arrays.asList("A", "B", "C");
288 try {
289 fixed.add("D");
290 } catch (UnsupportedOperationException e) {
291 log.info("Cannot change size of Arrays.asList() list");
292 }
293 }
294
295
296
297
298
299
300 public static void main(String[] args) {
301 defensiveCopyingExample();
302 choosingCollectionTypeExample();
303 immutabilityExample();
304 initializationExample();
305 nullHandlingExample();
306 iterationExample();
307 performanceExample();
308 commonPitfallsExample();
309 }
310 }