Pergunta

Eu tenho um milhão de linhas de dados em formato .txt. o formato é muito simples. Para cada linha:

user1,value1
user2,value2
user3,value3
user1,value4
...

Você sabe o que quero dizer. Para cada usuário, ele pode aparecer muitas vezes, ou aparecer apenas uma vez (nunca se sabe). Eu preciso descobrir todos os valores para cada usuário. Porque o usuário pode aparecer aleatoriamente, eu usei HashMap para fazê-lo. Ou seja: HashMap (chave: String, value: ArrayList). Mas para adicionar dados para o arrayList, eu tenho que usar constantemente HashMap get (key) para obter o arrayList, agregar valor a ele, em seguida, colocá-lo de volta para HashMap. Eu sinto que não é que muito eficiente. Alguém sabe uma maneira melhor de fazer isso?

Foi útil?

Solução

Você não precisa re-adicionar a volta ArrayList ao seu mapa. Se o ArrayList já existe em seguida, basta adicionar o seu valor.

Uma melhor aplicação pode parecer:

Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();

durante o processamento de cada linha:

String user = user field from line
String value = value field from line

Collection<String> values = map.get(user);
if (values==null) {
    values = new ArrayList<String>();
    map.put(user, values)
}
values.add(value);

Acompanhamento abril 2014 - Eu escrevi a resposta de volta original, em 2009, quando o meu conhecimento do Google Guava foi limitado. À luz de tudo o que o Google Guava faz, agora eu recomendo usar sua Multimap invés de reinventá-lo.

Multimap<String, String> values = HashMultimap.create();
values.put("user1", "value1");
values.put("user2", "value2");
values.put("user3", "value3");
values.put("user1", "value4");

System.out.println(values.get("user1"));
System.out.println(values.get("user2"));
System.out.println(values.get("user3"));

Saídas:

[value4, value1]
[value2]
[value3]

Outras dicas

Use Multimap do Google Collections. Ele permite que vários valores para a mesma chave

https: / /google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html

Os valores de ArrayList em seu HashMap são referências. Você não precisa de "colocá-lo de volta para HashMap". Você está operando no objeto que já existe como um valor no HashMap.

Se você não deseja importar uma biblioteca.

package util;    

import java.util.ArrayList;    
import java.util.HashMap;    
import java.util.List;    

/**    
 * A simple implementation of a MultiMap. This implementation allows duplicate elements in the the    
 * values. (I know classes like this are out there but the ones available to me didn't work).    
 */    
public class MultiMap<K, V> extends HashMap<K, List<V>> {    

  /**    
   * Looks for a list that is mapped to the given key. If there is not one then a new one is created    
   * mapped and has the value added to it.    
   *     
   * @param key    
   * @param value    
   * @return true if the list has already been created, false if a new list is created.    
   */    
  public boolean putOne(K key, V value) {    
    if (this.containsKey(key)) {    
      this.get(key).add(value);    
      return true;    
    } else {    
      List<V> values = new ArrayList<>();    
      values.add(value);    
      this.put(key, values);    
      return false;    
    }    
  }    
}    

Desde Java 8 você pode usar map.computeIfAbsent

https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#computeIfAbsent-K-java.util.function.Function-

Collection<String> values = map.computeIfAbsent(user, k -> new ArrayList<>());
values.add(value);

Eu acho que o que você quer é a Multimap. Você pode obtê-lo da coleção commons do apache, ou google-coleções.

http://commons.apache.org/collections/

http://code.google.com/p/google-collections/

"coleção semelhante a um mapa, mas que pode associar vários valores com uma única chave. Se você chamar put (K, V) duas vezes, com a mesma chave, mas valores diferentes, multimap contém mapeamentos da chave para ambos valores ".

Não foi possível encontrar alguma maneira fácil. MultiMap nem sempre é uma opção disponível. Então eu escrevi algo disso.

public class Context<K, V> extends HashMap<K, V> {

    public V addMulti(K paramK, V paramV) {
        V value = get(paramK);
        if (value == null) {
            List<V> list = new ArrayList<V>();
            list.add(paramV);
            put(paramK, paramV);
        } else if (value instanceof List<?>) {
            ((List<V>)value).add(paramV);
        } else {
            List<V> list = new ArrayList<V>();
            list.add(value);
            list.add(paramV);
            put(paramK, (V) list);
        }
        return paramV;
    }
}

que seria mais rápido se você usou um LinkedList em vez de um ArrayList, como o ArrayList terá de redimensionar quando se aproxima de capacidade.

Você também vai querer estimar adequadamente a capacidade da coleção de embrulho (HashMap ou Multimap) que você está criando para evitar rehashing repetitivo.

Como já mencionado, MultiMap é sua melhor opção.

Dependendo de suas necessidades de negócios ou restrições sobre o arquivo de dados, você pode querer considerar fazer um one-off de classificação do mesmo, para torná-lo mais otimizado para carregamento.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top