Last active
October 6, 2020 03:54
-
-
Save cesarvasconcelos/3f0b272034f9543195a030ddff56a39e to your computer and use it in GitHub Desktop.
Novos métodos Map Java 8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import java.util.*; | |
| public class NewMapMethodsJava8 { | |
| public static void main( String[] args ) | |
| { | |
| // nova versão do antigo método get() em mapas | |
| // antes do Java 8, Map.get() recebia a chave e | |
| // retornava valor. Mas, se chave não existisse, | |
| // get() retornaria null. Daí a ambiguidade: | |
| // nos mapas que permitem valores nulos, | |
| // o fato de retornar nulo me diz que | |
| // a chave existe, mas o valor associado é null? | |
| // ou quer dizer que a chave não existe? | |
| // Javadoc alertava isso! | |
| // dizendo que se o map permitir valores nulos, | |
| // pode ser que a chave ainda exista. E que | |
| // para saber se chave realmente existe, tinha de recorrer a containsKey() | |
| // If this map permits null values, then a return value of null | |
| // does not necessarily indicate that the map contains no mapping for the key; | |
| // it's also possible that the map explicitly maps the key to null. | |
| // The containsKey operation may be used to distinguish these two cases. | |
| // ou seja, no caso de mapas que permitem V nulos, | |
| // o get() antigo não me diz se a chave existe ou não | |
| // e tenho de recorrer a containsKey() | |
| Map<String, List<String>> map = new HashMap<>(); | |
| map.put( "Comédia", new ArrayList<>( Arrays.asList( "AceVentura", "AutoCompadecida" ) ) ); | |
| map.put( "Terror", null ); | |
| // antes tinha de usar uma view do Map.entrySet para poder imprimir o par-valor (entry) | |
| Set<Map.Entry<String, List<String>>> entries = map.entrySet(); | |
| for ( Map.Entry<String, List<String>> entry : entries ) | |
| System.out.println( "Key:" + entry.getKey() + " Value:" + entry.getValue() ); | |
| // podia evitar toda a digitação do tipo Map.Entry<String, List<String>> | |
| // se usasse a variável local com "var" Java 10 | |
| for ( var entry : map.entrySet() ) | |
| System.out.println( "Key:" + entry.getKey() + " Value:" + entry.getValue() ); | |
| // ou até evitar os dois anteriores bastando usar forEach() | |
| map.forEach( ( key, value ) -> { // forEach() também é novidade Java 8 | |
| System.out.println( "Key:" + key + " Value:" + value ); | |
| } ); | |
| System.out.println( map.get( "Comédia" ) ); | |
| System.out.println( map.get( "Terror" ) ); // chave existe mas valor é que é nulo | |
| System.out.println( map.get( "Romance" ) ); // problema-->chave não existe? ou valor é nulo? | |
| // após o Java 8 | |
| // um novo método: getOrDefault() | |
| // Returns the value to which the specified key is mapped, | |
| // or defaultValue if this map contains no mapping for the key. | |
| System.out.println( | |
| map.getOrDefault( "Comédia", new ArrayList<>() ) ); | |
| System.out.println( | |
| map.getOrDefault( "Terror", new ArrayList<>() ) ); // null: agora sei que valor é que é nulo | |
| System.out.println( | |
| map.getOrDefault( "Romance", new ArrayList<>() ) ); // []: agora sei que chave é que não existe | |
| // novo método: putIfAbsent como alternativa ao antigo método put() em mapas | |
| // no put de antes, programador tinha de verificar se chave não existe antes de inserir no mapa | |
| // pois se chave existisse, o antigo put() sobrescrevia/apagava o valor V associado à chave K | |
| // o put retornava o old V (que foi sobrescrito), caso chave existisse ou null. | |
| // Javadoc: Returns the previous value V associated with key, or null if there was no mapping for key. | |
| // (A null return can *also* indicate that the map previously associated null with key, | |
| // if the implementation supports null values.) | |
| // no Java 8, ficou mais fácil: o putIfAbsent adiciona, MAS ANTES verifica automati/te se | |
| // chave já existe. Se sim, não substitui o valor existente, mas o retornará. | |
| map.put( "Ficção", | |
| new ArrayList<>( Arrays.asList( "Matrix" ) ) ); // antes: se chave existir, V será substituído | |
| // depois: se chave existir, o valor existente não será substituído, mas retornado. | |
| // diferente de put() o método abaixo garante que ou | |
| // a lista original de filmes será retornada se K existir, | |
| // ou null, se K não existir (daí cuidado ao tentar fazer chaining com putIfAbsent(K,D).add()) | |
| // pois se K não existir, null será retornado e ao fazer null.add(), teremos: | |
| // java.lang.NullPointerException | |
| map.putIfAbsent( "Ficção", new ArrayList<>() ) | |
| .add( "StarWars" ); // putIfAbsent não sobrescreverá a lista original | |
| // cuidado ao tentar fazer chaining de chaining com putIfAbsent(K,D).add()) | |
| // map.putIfAbsent( "gênero-noir-francês", new ArrayList<>() ) | |
| // .add( "Filme Noir" ); // como K não existe, null.add() geraria java.lang.NullPointerException | |
| map.forEach( ( key, value ) -> { | |
| System.out.println( "Key:" + key + " Value:" + value ); | |
| } ); | |
| // novos métodos para replace() em mapas | |
| // replace(K key, V value): retorna V anterior | |
| // replace(K key ,V oldValue, V newValue): só faz replace se oldValue existir | |
| List<String> original = map.replace( "Ficção", new ArrayList<>( Arrays.asList( "Sinais" ) ) ); | |
| System.out.println( original ); | |
| map.forEach( ( key, value ) -> { | |
| System.out.println( "Key:" + key + " Value:" + value ); | |
| } ); | |
| map.replace( "Ficção", new ArrayList<>( Arrays.asList( "Sinais" ) ), | |
| new ArrayList<>( Arrays.asList( "HarryPotter" ) ) ); //ok | |
| map.replace( "Ficção", new ArrayList<>( Arrays.asList( "SINAIS" ) ), | |
| new ArrayList<>( Arrays.asList( "HarryPotter2" ) ) ); //não | |
| map.forEach( ( key, value ) -> { | |
| System.out.println( "Key:" + key + " Value:" + value ); | |
| } ); | |
| // um novo método:remove(Object key, Object value) | |
| // remove apenas se o par existir por completo | |
| // i.e., for realmente o value que existe no mapa | |
| // Removes the entry for the specified key | |
| // *only* if it is currently mapped to the specified value. | |
| map.remove( "Terror", new ArrayList<>( Arrays.asList( "A Freira" ) ) ); // não remove, mesmo que chave exista | |
| map.remove( "Terror", null ); // ok, remove pois o "par" está correto | |
| map.forEach( ( key, value ) -> { | |
| System.out.println( "Key:" + key + " Value:" + value ); | |
| } ); | |
| // uma nova família de métodos: compute, computeIfPresent e computeIfAbsent | |
| // basicamente são úteis até para adicionar e fazer remapeamentos | |
| // note que todas as versões devem retornar um novo V que foi computado | |
| // ou até o V que já existente no mapa antes, mas com uma computação a mais | |
| // 1a versão: recebe além da chave K, a Bifunção (K,V e retorna V), ou seja | |
| // V compute(K key, BiFunction<? superK, ? superV, ? extends V> function ) ou | |
| // compute(key, (key,value) -> newValue ) só roda função se K existir | |
| // bifunção irá receber K,V e retornar o valor V que será usado | |
| map.compute( "Comédia", ( k, v ) -> { | |
| if ( v.size() > 1 ) v.add( "Chaplin" ); | |
| else v.add( "Trapalhões" ); | |
| return v; | |
| } ); | |
| // e se key não existir? Exception in thread "main" java.lang.NullPointerException | |
| //map.compute( "Anime", ( k, v ) -> { | |
| // if ( v.size() > 1 ) v.add( "Japão1" ); | |
| // else v.add( "Japão2" ); | |
| // return v; | |
| //} ); | |
| // 2a versão: V computeifAbsent(K key, Function<? super K, ? super V> mappingfunction ) | |
| // ou apenas computeIfAbsent(key, key -> newValue ) | |
| // computar a função, mas apenas se chave não existir | |
| // se K existe, retorne V existente; | |
| // se K não existir, rode a função, e retorne o V computado | |
| // i.e., função, CASO SEJA RODADA, deve receber K e retornar um o valor V computado | |
| // note abaixo que a função lambda será executada apenas 1a vez, pois key Francês não existe | |
| map.computeIfAbsent( "Francês", k -> new ArrayList<>() ) // como K não existe, a função (k->v) | |
| .add( "Jaci Borrô" );// será rodada para produzir valor qdo K não existir | |
| // dica: no caso acima, note que é bem mais seguro fazer chaining de add com computeIfAbset(K,V).add() | |
| // do que com putIfAbsent; pois putIfAsent retorna null *quando K não existe* | |
| // o que resultará em risco de *null*.add() com NullPointerException | |
| // por outro lado, computeIfAbset(K,V) retornará a lista vazia (V que foi computado) quando K não existe* | |
| // daí posso fazer add sem problemas de NullPointerException | |
| // já na 2a vez abaixo note que, se chave existe, computeIfAbsent não faz nada | |
| // e apenas retorna o V existente. Daí posso dar add facilmente | |
| map.computeIfAbsent( "Francês", k -> new ArrayList<>() ) // se chave existe, retorne V existente | |
| .add( "Paris 2" ); // uma boa sintaxe anternativa à putIfAbsent | |
| map.computeIfAbsent( "Francês", k -> new ArrayList<>() ) // se chave existe, retorne V existente | |
| .add( "Sauvignon 3" ); // dai posso pegar V existente e inserir | |
| // outro exemplo: note abaixo que a função lambda será executada, pois key Ação não existe | |
| map.computeIfAbsent( "Ação", k -> new ArrayList<>( Arrays.asList( "Comandos em " + k.toUpperCase() ) ) ); | |
| // agora abaixo note que a função lambda NÃO será executada, pois key Ação já existia | |
| List<String> açãoList = map.computeIfAbsent( "Ação", k -> new ArrayList<>() ); | |
| System.out.println(açãoList); | |
| // 3a versão: V computeIfPresent(K key, BiFunction<? superK, ? superV, ? extends V> function ) | |
| // ou apenas computeIfPresent(key, (key,value) -> newValue ) | |
| // computar a função, apenas se chave existir | |
| // bifunção irá receber K,V e retornar um novo valor V a ser usado | |
| // abaixo, como key Romance não existe, bifunção não será executada | |
| map.computeIfPresent( "Romance", ( k, v ) -> Arrays.asList( "Linda Mulher" ) ); | |
| // agora abaixo note que a função lambda será executada, pois key Ação já existia | |
| map.computeIfPresent( "Ação", ( k, v ) -> Arrays.asList( v.get( 0 ), "Rambo" ) ); | |
| map.forEach( ( key, value ) -> { | |
| System.out.println( "Key:" + key + " Value:" + value ); | |
| } ); | |
| // novo método merge: V merge(K key, V newValue, BiFunction<? superV, ? superV, ? extends V> function) | |
| // se chave não estiver no mapa, apenas adiciona o par K key, V newValue | |
| // note que BiFunction existe para o caso de a key a entrar no merge já existir | |
| // se não existir, merge apenas adiciona. Mas, se key existir, BiFunction recebe três valores: | |
| // o V já existente no map destinatário, o V que quer entrar no mapa e o V resultante do merge | |
| Map<String, List<String>> map2 = new HashMap<>(); | |
| map2.put( "Comédia", new ArrayList<>( Arrays.asList( "Trapalhões", "Deadpool" ) ) ); // veja que comédia existirá em map | |
| map2.put( "Western", new ArrayList<>( Arrays.asList( "Kill Bill", "Faroeste 2" ) ) ); // este não existe | |
| // quero percorrer o mapa2, par a par, e fazer merge em map1 | |
| map2.forEach( | |
| // abaixo, um BiConsumer representando o par K,V de map2 que quero jogar em map1; BiConsumer não retorna nada | |
| ( keyMapa2, valueMap2 ) -> { | |
| map.merge( keyMapa2, valueMap2, // <--- dois primeiros parâmetros de merge naturalmente são: o que inserir em map1? | |
| // se keyMap2 não existir em map1, merge só insere par <keyMapa2, valueMap2> em map1 | |
| // MAS, se keyMap2 já existir em map1, tenho: | |
| // A) uma lista de filmes já existente em map1 | |
| // B) uma nova lista de map2 querendo entrar em map1 | |
| // C) uma lista resultante/final que irá prevalecer no final do merge | |
| // Solução? use BiFunction<VMap1,VMap2,VFinal> dirá o que fazer com esse par<V1,V2> existentes | |
| // o BiFunction<VMap1,VMap2,VFinal> vai ajudar a resolver o conflito sobre o que fazer | |
| // com os valores dos mapas de origem e de destino | |
| ( existingValueMap1, existingValueMap2 ) -> { | |
| existingValueMap1.addAll( existingValueMap2 ); | |
| return existingValueMap1; | |
| } | |
| ); | |
| } | |
| ); | |
| map.forEach( ( key, value ) -> { | |
| System.out.println( "Key:" + key + " Value:" + value ); | |
| } ); | |
| // um novo método:replaceAll(BiFunction function) | |
| // substituirá todos os V existentes do mapa por um novo V (retornado pela BiFunction) | |
| // ou seja, replaceAll( (K, oldV) -> newV ) | |
| map.replaceAll( ( keyStrGênero, oldListV ) -> { | |
| // quero manter os V da lista existente | |
| ArrayList<String> newListV = new ArrayList<>( | |
| Optional.ofNullable( oldListV ) // caso V existente seja null | |
| .orElse( List.of() ) // entao lista nova | |
| ); | |
| newListV.add( "Xuxa" ); // e adicionar 1 item de Xuxa em todas as listas V | |
| return newListV; | |
| } ); | |
| // ou até substituir todas as listas por uma newListV | |
| map.replaceAll( ( strKey, oldList ) -> Collections.singletonList( "Trapalhões" ) ); | |
| map.forEach( ( key, value ) -> { | |
| System.out.println( "Key:" + key + " Value:" + value ); | |
| } ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment