Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Исследование роли accessOrder в жизни


Автор: Royan 22.4.2008, 11:18
Есть у данной исплементации Map конструктор со следующей сигнатурой: LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) 

Я решил поэкспериментировать, вот мои результаты:

Код

import java.util.LinkedHashMap;

public class LinkedHashMapTest {
    public static void main(String[] args) {
        final int LEN = 10;
        LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>(
                LEN, 0.75f, true);
        for (int i = 0; i < LEN; i++) {
            map.put(i, new String(">" + i));
        }
        
        for (Integer i : map.keySet()) {
            System.err.println("=" + map.get(i));
            System.err.println("=" + map.get(i+1));
            System.err.println("=" + map.get(i+2));
            System.err.println("=" + map.get(i+3));
            System.err.println("=" + map.get(i+4));
            System.err.println("=" + map.get(i+5));
            System.err.println("=" + map.get(i+6));
            System.err.println("=" + map.get(i+7));
            System.err.println("=" + map.get(i+8));
            break;
        }
        
        for (Integer i : map.keySet()) {
            System.err.println("!" + map.get(i));
            break;
        }        
    }
}


Output:
Цитата

=>0
=>1
=>2
=>3
=>4
=>5
=>6
=>7
=>8
!>9


Если к-то не заметил, то в случае когда параметр accessOrder выставлен в true, то всякий раз когда мы пользуемся итератором, последний элемент к которому произошло обращение при помощи LinkedHashMap#get(), LinkedHashMap#put() и LinkedHashMap#putAll() переходит в конец списка (в случае putAll() для каждого добавленного элемента).

Объяснил коряво ну суть такая. Если accessOrder в true, и мы внутри цикла итерируем по LinkedHashMap, то всякий раз когда мы делаем вызов вышеописанных методов внутри нашего цикла, то следующий раз когда мы будем итерировать то начнем с того элемента который дернули последним.

Именно поэтому в строке 26 авыводится 9, а не 0 (как было бы если бы accessOrder был выставлен в false) break'и в циклах стоят, дабы не спровоцировать ConcurrentModificationException, поскольку опять же обращение get(), put() и putAll() модифицирует внутреннюю структуру списка.

Еще одно дополнение, вызовы get() должны быть последовательным поэтому если сразу дернуть, к примеру, 2-й элемент, то все будет работать в обычных условиях:

Код

import java.util.LinkedHashMap;

public class LinkedHashMapTest {
    public static void main(String[] args) {
        final int LEN = 10;
        LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>(
                LEN, 0.75f, true);
        for (int i = 0; i < LEN; i++) {
            map.put(i, new String(">" + i));
        }
        
        for (Integer i : map.keySet()) {
            System.err.println("=" + map.get(i+2));
            break;
        }
        
        for (Integer i : map.keySet()) {
            System.err.println("!" + map.get(i));
            System.err.println("!" + map.get(i+1));
            System.err.println("!" + map.get(i+2));
            System.err.println("!" + map.get(i+3));
            System.err.println("!" + map.get(i+4));
            break;
        }
    }
}



Output:
Цитата

=>2
!>0
!>1
!>2
!>3
!>4


Все это я затеял с тем, чтобы понять зачем вообще весь этот огород было городить? В каких задачах нужна подобная функциональность?

Автор: Andy_L20066 22.4.2008, 12:33
интересный вопрос.... ну могу сказать следующее:
если accessOrder у нас true и выполняется (Integer i : map.keySet()) тогда переменная 'i' будет постоянно отвечать за индекс первого елемента в мапе... то есть это может быть 9, как в первом случае... потому что этот елемент сейчас на первом месте в мапе... тоесть вернется елемент с ключем 9...
или если во втором случае у нас был доступ сначала ко второму елементу(map.get(i+2)), потом map.get(i) вернуло 0... ну правильно... потому что второй елемент поместился в конец мапы.... а на первом месте сейчас елемент с ключем 0....если accessOrder у нас false - тогда (Integer i : map.keySet()) будет нулевой... и будет возвращатся елемент с ключем '0'...
тоесть с тем какие елементы  в даный момент возвращаются, на мой взгляд - все логично...

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

поправьте если может чьо не так изложил...


Автор: LSD 22.4.2008, 16:32
Цитата(Royan @  22.4.2008,  12:18 Найти цитируемый пост)
Все это я затеял с тем, чтобы понять зачем вообще весь этот огород было городить? В каких задачах нужна подобная функциональность?

Там же в доке достаточно ясно написано - для организации LRU кешей, списков и т.п.

Автор: Royan 24.4.2008, 12:13
Цитата(LSD @  22.4.2008,  13:32 Найти цитируемый пост)
LRU кешей, списков и т.п.

Окей возьмем к примеру LRU кэш. Например мне нужно вытащить из LinkedHashMap'а 5-ый элемент... это что же получается - для того, чтобы добраться до него я должен буду проитерировать по элементам с 1 по 4-ый, чтобы потом взять сверху 5-ый? Тогда теряется суть кэша. В какой-то одной стороне должны оказываться самые часто используемые элементы, в другой самые редко используемые, а тут я взял и перебрал все с 1 по 4 как будто они мне были нужны.

Автор: LSD 24.4.2008, 12:54
Цитата(Royan @  24.4.2008,  13:13 Найти цитируемый пост)
Например мне нужно вытащить из LinkedHashMap'а 5-ый элемент... это что же получается - для того, чтобы добраться до него я должен буду проитерировать по элементам с 1 по 4-ый, чтобы потом взять сверху 5-ый? Тогда теряется суть кэша. В какой-то одной стороне должны оказываться самые часто используемые элементы, в другой самые редко используемые, а тут я взял и перебрал все с 1 по 4 как будто они мне были нужны.

Из Map никто не вытаскивает элементы по номеру, т.к. в общем случае (например при использовании HashMap) неизвестно что там будет под пятым номером. Элементы из Map вытаскиваются по ключу. 
Итератор по Map используется или для полного перебора всех элементов, или для перебора их в определенном порядке (SortedMap или LRU Map) который индивидуален для каждого типа Map.

Автор: Royan 27.4.2008, 20:15
Цитата(LSD @  24.4.2008,  09:54 Найти цитируемый пост)
Итератор по Map используется или для полного перебора всех элементов, или для перебора их в определенном порядке (SortedMap или LRU Map) который индивидуален для каждого типа Map.

Проблема в том, что в LRU реализации LinkedHashMap невозможно проитерировать более одного элемента за раз, так как внутренняя структура карты меняется после каждого выхова метода get

Автор: LSD 27.4.2008, 22:57
Цитата(Royan @  27.4.2008,  21:15 Найти цитируемый пост)
Проблема в том, что в LRU реализации LinkedHashMap невозможно проитерировать более одного элемента за раз, так как внутренняя структура карты меняется после каждого выхова метода get

Возможно, просто надо использовать правильный итератор smile 
Код

final int LEN = 10;
LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>(LEN, 0.75f, true);
for(int i = 0; i < LEN; i++)
{
  map.put(i, "(" + i + ")");
}

for(Map.Entry<Integer, String> entry : map.entrySet())
  System.out.println(entry.getKey() + " => " + entry.getValue());

Автор: Royan 28.4.2008, 00:35
Цитата(LSD @  27.4.2008,  19:57 Найти цитируемый пост)
Возможно, просто надо использовать правильный итератор

Итератор заработал - эффект пропал, это я, конечно, не к тому, чтобы ты полез дебажить smile просто забавная фукнциональность

Код

import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapTest {
    public static void main(String[] args) {
        final int LEN = 10;
        LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>(
                LEN, 0.75f, true);
        for (int i = 0; i < LEN; i++) {
            map.put(i, new String(">" + i));
        }
        
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.err.println("=" + entry.getValue());
            if(entry.getKey() == 2) {
                break;
            }
        }
        
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.err.println("=" + entry.getValue());
            if(entry.getKey() == 2) {
                break;
            }
        }     
    }
}


Output:
Цитата

=>0
=>1
=>2
=>0 // по идее тут я должен был продолжить итерировать с 3
=>1
=>2

Автор: LSD 28.4.2008, 11:38
Цитата(Royan @  28.4.2008,  01:35 Найти цитируемый пост)
по идее тут я должен был продолжить итерировать с 3

Клево придумано smile В одном методе, мы проитерировали коллекцию до середины, в другом до проитерировали с середины до конца. А дальше ничего уже больше не итерируем, т.к. дошли до конца. Так? smile

То что ты хочешь, реализуется так:
Код

import java.util.*;

public class Test
{
  public static void main(String[] args) throws Exception
  {
    final int LEN = 10;
    LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>(LEN, 0.75f, true);
    for(int i = 0; i < LEN; i++)
      map.put(i, "(" + i + ")");

    Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();

    while(iterator.hasNext())
    {
      Map.Entry<Integer, String> entry = iterator.next();
      System.out.println("=" + entry.getValue());
      if(entry.getKey() == 2)
        break;
    }

    while(iterator.hasNext())
    {
      Map.Entry<Integer, String> entry = iterator.next();
      System.out.println("=" + entry.getValue());
      if(entry.getKey() == 2)
        break;
    }
  }
}


Есть такая штука ListIterator, вот он позволяет указывать с какого места начати итерацию, и позволяет итерировать как вперед, так и назад. Но существует, только для List.

P.S. И не пиши new String(">" + i) это совершенно избыточный код, приводящий к созданию лишних объектов. Вполне достаточно ">" + i.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)