Самый эффективный способ увеличить значение Карты в Java

Вы должны изменить код вашей серверной части, как указано ниже

public class CorsResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,   ContainerResponseContext responseContext)
    throws IOException {
        responseContext.getHeaders().add("Access-Control-Allow-Origin","*");
        responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");

  }
}
347
задан gregory 20 September 2008 в 14:11
поделиться

15 ответов

Некоторые результаты испытаний

я добрался, много хороших ответов на этот вопрос - благодарит людей - таким образом, я решил запустить некоторые тесты и фигуру, какой метод является на самом деле самым быстрым. Эти пять методов, которые я протестировал, являются ими:

  • методика "ContainsKey", которую я представил в вопрос
  • метод "TestForNull", предложенный Aleksandar Dimitrov
  • метод "AtomicLong", предложенный Hank Gay
  • метод "Находки", предложенный jrudolph
  • метод "MutableInt", предложенный phax.myopenid.com

Метод

Здесь, - то, что я сделал...

  1. создал пять классов, которые были идентичны за исключением различий, показанных ниже. Каждый класс должен был выполнить операцию, типичную для сценария, который я представил: открытие файла 10 МБ и чтение его в, затем выполняя подсчет частот всех словоупотреблений в файле. Так как это заняло в среднем только 3 секунды, у меня был он, выполняют подсчет частот (не ввод-вывод) 10 раз.
  2. синхронизировал цикл 10 повторений, но не операция ввода-вывода и записала потраченное общее время (в секунды часов) по существу использование метод Ian Darwin в Поваренной книге Java .
  3. выполнил все пять тестов последовательно, и затем сделал это еще три раза.
  4. составил в среднем четыре результата для каждого метода.

Результаты

я представлю результаты сначала и код ниже для тех, кому интересно.

метод ContainsKey был, как ожидалось, самым медленным, таким образом, я дам скорость каждого метода по сравнению со скоростью того метода.

  • ContainsKey: 30,654 секунды (базовая линия)
  • AtomicLong: 29,780 секунд (в 1.03 раза более быстрых)
  • TestForNull: 28,804 секунд (в 1.06 раза более быстрых)
  • Находка: 26,313 секунд (в 1.16 раза более быстрых)
  • MutableInt: 25,747 секунд (в 1.19 раза более быстрых)

Заключения

, казалось бы, что только метод MutableInt и метод Находки значительно быстрее в том единственном, которое они дают повышению производительности больше чем 10%. Однако, если поточная обработка является проблемой, AtomicLong мог бы быть более привлекательным, чем другие (я не действительно уверен). Я также выполнил TestForNull с final переменные, но различие было незначительно.

Примечание, что я не представил использование памяти в различных сценариях. Я был бы рад получить известие от кого-либо, у кого есть хорошее понимание того, как методы MutableInt и Находки, вероятно, влияли бы на использование памяти.

Лично, я нахожу метод MutableInt самым привлекательным, так как он не требует загрузки никаких сторонних классов. Таким образом, если я не обнаруживаю проблемы с ним, это - путь, которым я, скорее всего, пойду.

код

Вот решающий код из каждого метода.

находка ContainsKey

import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
int count = freq.containsKey(word) ? freq.get(word) : 0;
freq.put(word, count + 1);

TestForNull

import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
Integer count = freq.get(word);
if (count == null) {
    freq.put(word, 1);
}
else {
    freq.put(word, count + 1);
}

AtomicLong

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
...
final ConcurrentMap<String, AtomicLong> map = 
    new ConcurrentHashMap<String, AtomicLong>();
...
map.putIfAbsent(word, new AtomicLong(0));
map.get(word).incrementAndGet();

import gnu.trove.TObjectIntHashMap;
...
TObjectIntHashMap<String> freq = new TObjectIntHashMap<String>();
...
freq.adjustOrPutValue(word, 1, 1);

MutableInt

import java.util.HashMap;
import java.util.Map;
...
class MutableInt {
  int value = 1; // note that we start at 1 since we're counting
  public void increment () { ++value;      }
  public int  get ()       { return value; }
}
...
Map<String, MutableInt> freq = new HashMap<String, MutableInt>();
...
MutableInt count = freq.get(word);
if (count == null) {
    freq.put(word, new MutableInt());
}
else {
    count.increment();
}
350
ответ дан Community 4 November 2019 в 10:06
поделиться

Я использовал бы Наборы Apache Ленивая Карта (для инициализации значений к 0) и использовал бы MutableIntegers от Apache Lang как значения в той карте.

Самая большая стоимость должна искать карту дважды в Вашем методе. В моем необходимо сделать это только однажды. Просто получите значение (оно будет инициализировано, если отсутствующий), и увеличивают его.

1
ответ дан jb. 4 November 2019 в 10:06
поделиться

@Vilmantas Baranauskas: Относительно этого ответа я прокомментировал бы, были ли у меня точки представителя, но я не делаю. Я хотел отметить что Встречный класс, определенный, там не ориентировано на многопотоковое исполнение, поскольку не достаточно просто синхронизировать inc (), не синхронизируя значение (). Другие потоки, называя значение (), как гарантируют, не будут видеть значение, если происхождение - перед отношениями не было установлено с обновлением.

1
ответ дан Alex Miller 4 November 2019 в 10:06
поделиться

Различные примитивные обертки, например, Integer неизменны, таким образом, существует действительно не более краткий способ сделать то, что Вы спрашиваете , если Вы не можете сделать это с чем-то как AtomicLong. Я могу дать этому движение через минуту и обновление. BTW, Хеш-таблица часть эти Платформа Наборов .

1
ответ дан Hank Gay 4 November 2019 в 10:06
поделиться

Я думаю, что Вашим решением был бы стандартный путь, но - поскольку Вы отметили себя - это - вероятно, не самый быстрый возможный путь.

можно посмотреть Находка GNU . Это - библиотека, которая содержит все виды быстрых примитивных Наборов. Ваш пример использовал бы TObjectIntHashMap, который имеет метод adjustOrPutValue, который делает точно, что Вы хотите.

3
ответ дан jrudolph 4 November 2019 в 10:06
поделиться

Вместо того, чтобы назвать containsKey () это быстрее только для вызова map.get и проверки, если возвращенное значение является пустым или нет.

    Integer count = map.get(word);
    if(count == null){
        count = 0;
    }
    map.put(word, count + 1);
5
ответ дан Glever 4 November 2019 в 10:06
поделиться

Вы уверены, что это - узкое место? Вы сделали какой-либо анализ производительности?

Попытка с помощью профилировщика NetBeans (его свободное и встроенный в NB 6.1) для рассмотрения горячих точек.

Наконец, обновление JVM (говорят от 1.5-> 1.6) часто является дешевым усилителем производительности. Даже обновление в номере сборки может обеспечить хорошие повышения производительности. Если Вы работаете на Windows, и это - приложение класса сервера, используйте - сервер на командной строке для использования JVM Горячей точки Сервера. На машинах Linux и Соляриса это автоматически обнаруживается.

3
ответ дан 4 November 2019 в 10:06
поделиться

Вращение памяти может быть проблемой здесь, начиная с каждой упаковки интервала, больше, чем, или равняться 128 причинам объектному выделению (см. Integer.valueOf (интервал)). Хотя сборщик "мусора" очень эффективно имеет дело с недолгими объектами, производительность пострадает до некоторой степени.

, Если Вы знаете, что количество инкрементов составило завещание в основном, превосходят численностью количество ключей (=words в этом случае), рассматривают использование международного держателя вместо этого. Phax уже представил код для этого. Здесь это снова с двумя изменениями (класс держателя, сделанный набором статического и начального значения к 1):

static class MutableInt {
  int value = 1;
  void inc() { ++value; }
  int get() { return value; }
}
...
Map<String,MutableInt> map = new HashMap<String,MutableInt>();
MutableInt value = map.get(key);
if (value == null) {
  value = new MutableInt();
  map.put(key, value);
} else {
  value.inc();
}

при необходимости в экстремальной производительности ищите Реализацию Map, которая непосредственно адаптируется к примитивным типам значения. jrudolph упомянул Находка GNU .

Между прочим, хороший критерий поиска для этого предмета является "гистограммой".

7
ответ дан volley 4 November 2019 в 10:06
поделиться

Иначе создал бы изменяемое целое число:

class MutableInt {
  int value = 0;
  public void inc () { ++value; }
  public int get () { return value; }
}
...
Map<String,MutableInt> map = new HashMap<String,MutableInt> ();
MutableInt value = map.get (key);
if (value == null) {
  value = new MutableInt ();
  map.put (key, value);
} else {
  value.inc ();
}

, конечно, это подразумевает создание дополнительного объекта, но издержки по сравнению с созданием Целого числа (даже с Integer.valueOf) не должны быть так.

18
ответ дан Philip Helger 4 November 2019 в 10:06
поделиться

Необходимо знать о том, что исходная попытка

int count = map.containsKey(word) ? map.get(word) : 0;

содержит две потенциально дорогих операции на карте, а именно, containsKey и get. Первый выполняет операцию, потенциально симпатичную подобный последнему, таким образом, Вы делаете ту же работу дважды !

при рассмотрении API для Карты, get операции обычно возвращаются null, когда карта не содержит требуемый элемент.

Примечание, что это сделает решение как [1 132]

map.put( key, map.get(key) + 1 );

опасным, так как оно могло бы уступить NullPointerException с. Необходимо проверить на null сначала.

Также примечание , и это очень важно, тот HashMap, s может содержать nulls по определению. Так не каждый возвращенный null говорит, что "нет такого элемента". В этом отношении, containsKey ведет себя по-другому от [1 113] в фактическом сообщении Вам ли существует такой элемент. Обратитесь к API для деталей.

Для Вашего случая, однако, Вы не могли бы хотеть различать сохраненный null и "noSuchElement". Если Вы не хотите разрешать null с, Вы могли бы предпочесть Hashtable. Используя библиотеку-оболочку, как был уже предложен в других ответах, могло бы быть лучшее решение ручной обработки, в зависимости от сложности Вашего приложения.

Для завершения ответа (и я забыл вставлять это сначала благодаря функции редактирования!), лучший способ сделать его исходно, к [1 117] в final переменная, проверьте на [1 119], и put это въезжает задним ходом с 1. Переменная должна быть final, потому что это неизменно так или иначе. Компилятору, возможно, не понадобилась бы эта подсказка, но его более ясное тот путь.

final HashMap map = generateRandomHashMap();
final Object key = fetchSomeKey();
final Integer i = map.get(key);
if (i != null) {
    map.put(i + 1);
} else {
    // do something
}

, Если Вы не хотите полагаться на автоупаковку, необходимо сказать что-то как [1 123] вместо этого.

21
ответ дан Aleksandar Dimitrov 4 November 2019 в 10:06
поделиться

Это всегда - хорошая идея посмотреть Google Collections Library для такого рода вещи. В этом случае Мультимножество добьется цели:

Multiset bag = Multisets.newHashMultiset();
String word = "foo";
bag.add(word);
bag.add(word);
System.out.println(bag.count(word)); // Prints 2

существуют подобные Карте методы для итерации по ключам/записям, и т.д. Внутренне реализация в настоящее время использует HashMap<E, AtomicInteger>, таким образом, Вы не понесете расходы упаковки.

25
ответ дан Chris Nokleberg 4 November 2019 в 10:06
поделиться

@Hank Gay

Как продолжение моего собственного (довольно бесполезного) комментария: Находка похожа на способ пойти. Если по любой причине Вы хотели придерживаться стандартного JDK, ConcurrentMap и , AtomicLong может сделать код крошечным , укусил более хороший, хотя YMMV.

    final ConcurrentMap<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong>();
    map.putIfAbsent("foo", new AtomicLong(0));
    map.get("foo").incrementAndGet();

уедет 1 как значение в карте для foo. Реалистично, увеличенное дружелюбие к поточной обработке - все, что этот подход должен рекомендовать его.

31
ответ дан Hank Gay 4 November 2019 в 10:06
поделиться

Существует несколько подходов:

  1. Использование Сумка alorithm как наборы содержится в Google Collections.

  2. Создают изменяемый контейнер, который можно использовать в Карте:


    class My{
        String word;
        int count;
    }

И помещенное использование ("слово", новое Мой ("Word")); Тогда можно проверить, существует ли это и инкремент при добавлении.

Стараются не прокручивать Ваше собственное решение с помощью списков, потому что, если Вы получаете innerloop поиск и сортировка, Ваша производительность будет вонять. Первое решение HashMap на самом деле довольно быстро, но надлежащее как найденный в Google Collections, вероятно, лучше.

слова подсчета с помощью Google Collections, выглядит примерно так:



    HashMultiset s = new HashMultiset();
    s.add("word");
    s.add("word");
    System.out.println(""+s.count("word") );


Используя HashMultiset довольно изящно, потому что алгоритм сумки, в чем Вы нуждаетесь при подсчете слов.

3
ответ дан tovare 4 November 2019 в 10:06
поделиться

Я предлагаю использовать Карту Java 8:: вычислите (). Это рассматривает случай, когда ключ не существует, также.

Map.compute(num, (k, v) -> (v == null) ? 1 : v + 1);
0
ответ дан 23 November 2019 в 00:28
поделиться

Функциональная библиотека Java TreeMap структура данных имеет метод обновления в последней заголовке ствола:

public TreeMap<K, V> update(final K k, final F<V, V> f)

Пример использования:

import static fj.data.TreeMap.empty;
import static fj.function.Integers.add;
import static fj.pre.Ord.stringOrd;
import fj.data.TreeMap;

public class TreeMap_Update
  {public static void main(String[] a)
    {TreeMap<String, Integer> map = empty(stringOrd);
     map = map.set("foo", 1);
     map = map.update("foo", add.f(1));
     System.out.println(map.get("foo").some());}}

Эта программа печатает «2».

1
ответ дан 23 November 2019 в 00:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: