Home > Blockchain >  How to change pairs in a map?
How to change pairs in a map?

Time:01-14

I have a Map<Integer, Character> alphabetMap, which contains links from number to alphabet.

For example: [1: 'A', 2: 'B', ... , 26: 'Z']

I have a rotate() method, which should put the entries with changing links

After first using of the method my map should be [1: 'Z', 2: 'A', 3: 'B', ... , 26: 'Y']

Here is my current realisation:

public void rotate() {
    final Map<Integer, Character> tempMap = new HashMap<>();
    alphabetMap.forEach((key, value) -> tempMap.put(key == 26 ? 1 : key   1, value));
    alphabetMap = tempMap;
}

Is there any another way/algorhitm to "rotate" my entries quicker?

CodePudding user response:

For "faster" rotation the map could be replaced with a list and then method Collections.rotate could be used for this purpose. Then the list elements may be accessed by index in range [0..25].

Or a small wrapper class may be implemented:

static class MyCharMap {
    private List<Character> chars = IntStream
        .rangeClosed('A', 'Z')
        .mapToObj(c -> (char)c)
        .collect(Collectors.toList());
        
    public void rotate() {
        Collections.rotate(chars, 1);
    }
    
    public Character get(Integer i) {
        assert(1 <= i && i <= 26);
        return chars.get(i - 1);
    }
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(4 * chars.size()   2);
        sb.append('{');
        for (int i = 0, n = chars.size(); i < n; i  ) {
            if (i > 0) sb.append(", ");
            sb.append(i   1).append(':').append(chars.get(i));
        }
        sb.append('}');
        return sb.toString();
    }
}

Test:

MyCharMap chars = new MyCharMap();
        
chars.rotate();
chars.rotate();
System.out.println(chars);
System.out.println(chars.get(1));

Output:

{1:Y, 2:Z, 3:A, 4:B, 5:C, 6:D, 7:E, 8:F, 9:G, 10:H, 11:I, 12:J, 13:K, 14:L, 15:M, 16:N, 17:O, 18:P, 19:Q, 20:R, 21:S, 22:T, 23:U, 24:V, 25:W, 26:X}
Y

CodePudding user response:

There is nothing wrong with your approach if using a map. But since rotations must be relative to some order and maps are unordered, you may want to use a List as suggested by Alex Rudenko.

Here is another alternative using maps. It permits left or right rotation by any amount (based on the sign) for a supplied map that has sequential integer keys starting at 1. It also adjusts for counts exceeding the size by using the remainder operator. For left or right rotation offsets are simply calculated and the map altered and returned for subsequent processing.

BiFunction<Map<Integer, Character>, Integer, Map<Integer, Character>> rotate =
        (mp, cnt) -> {
            int size = mp.values().size();
            int count = cnt < 0 ? size   (cnt % size) - 1 :
                    cnt - 1;
            return mp.entrySet().stream()
                    .map(e -> new AbstractMap.SimpleEntry<>(
                            (e.getKey()   count) % size   1,
                            e.getValue()))
                    .collect(Collectors.toMap(e -> e.getKey(),
                            e -> e.getValue()));
        };

System.out.println(map);     // original map - 10 elements
map = rotate.apply(map,1);   // right one - starting at J
System.out.println(map);        
map = rotate.apply(map,-2);  // left two, skipping A, going to B
System.out.println(map);
map = rotate.apply(map, -21);// Essentially left one going to C
System.out.println(map);
map = rotate.apply(map, 22); // Essentially right two going to A
System.out.println(map);

prints

{1=A, 2=B, 3=C, 4=D, 5=E, 6=F, 7=G, 8=H, 9=I, 10=J}
{1=J, 2=A, 3=B, 4=C, 5=D, 6=E, 7=F, 8=G, 9=H, 10=I}
{1=B, 2=C, 3=D, 4=E, 5=F, 6=G, 7=H, 8=I, 9=J, 10=A}
{1=C, 2=D, 3=E, 4=F, 5=G, 6=H, 7=I, 8=J, 9=A, 10=B}
{1=A, 2=B, 3=C, 4=D, 5=E, 6=F, 7=G, 8=H, 9=I, 10=J}

The lambda version could easily be replaced by a regular method that takes a single rotate value and works on a fixed map.

CodePudding user response:

As stated by @code-apprentice in a comment

Since the keys are in numerical order, I suggest using an array or a list instead of a map.

If in case you still want to return a map, you could use the Collections.rotate(...) method in conjunction with reconstructing the expected map.

BiFunction<List<?>, Integer, Map<Integer, ?>> rotate =
        (list, distance) -> {

            Function<List<?>, Map<Integer, ?>> setMap = (arrayList) -> IntStream.range(0, arrayList.size())
                    .boxed()
                    .collect(Collectors.toMap(arrayList::get, Function.identity()))
                    .entrySet()
                    .stream()
                    .peek(e -> e.setValue(e.getValue()   1))
                    .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

            Collections.rotate(list, distance);
            return setMap.apply(list);

        };

System.out.println(rotate.apply(Arrays.asList('A', 'B', 'C', 'D'), 1)); // {1=D, 2=A, 3=B, 4=C}
System.out.println(rotate.apply(Arrays.asList("Peter", "James", "Sam", "Tiffany", "Mathew"), -3)); // {1=Tiffany, 2=Mathew, 3=Peter, 4=James, 5=Sam}

CodePudding user response:

Based on Alex Rudenko's answer, you can create a custom map that uses Collections.rotate(...) behind the scenes.

Custom map:

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

class MyMap {
    private static int counter = 0;
    private final Map<Integer, ?> initMap;
    private final List<?> list;
    private Map<Integer, ?> rotatedMap;

    public MyMap(List<?> list) {
        this.list = list;
        this.initMap = setMap(this.list);
        this.rotatedMap = cloneMap(this.initMap);
    }

    private Map<Integer, ?> setMap(List<?> list) {
        return IntStream.range(0, list.size())
                .boxed()
                .collect(Collectors.toMap(list::get, Function.identity()))
                .entrySet()
                .stream()
                .peek(e -> e.setValue(e.getValue()   1))
                .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
    }

    private Map<Integer, ?> cloneMap(Map<Integer, ?> map) {
        return map.entrySet()
                .stream()
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    public Map<Integer, ?> get() {
        counter = 0;
        return rotatedMap;
    }

    public MyMap reset() {
        counter = 0;
        this.rotatedMap = cloneMap(this.initMap);
        return this;

    }

    public MyMap rotate() {
        return rotateMap(  counter);
    }

    public MyMap rotate(int distance) {
        return rotateMap(distance);
    }

    private List<?> cloneList(List<?> list) {
        return list.stream().map(e -> e).collect(Collectors.toList());
    }

    private MyMap rotateMap(int distance) {
        List<?> list = cloneList(this.list);
        Collections.rotate(list, distance);
        this.rotatedMap = setMap(list);
        return this;
    }

    public String toString() {
        return rotatedMap.toString();
    }
}

Example 1 (Rotate twice, Reset rotation, Rotate twice.):

// Rotate twice, Reset rotation, Rotate twice.
MyMap myCharMap1 = new MyMap(Arrays.asList('A', 'B', 'C', 'D'));
System.out.println(myCharMap1
        .rotate(2)
        .reset()
        .rotate().rotate()
        .get()); // {1=C, 2=D, 3=A, 4=B}

Example 2 (Rotate thrice. Can rotate a String list):

//Rotate thrice.
MyMap myMap = new MyMap(Arrays.asList("Peter", "James", "Sam", "Tiffany", "Mathew"));
System.out.println(myMap.rotate(3).get()); // {1=Sam, 2=Tiffany, 3=Mathew, 4=Peter, 5=James}

Example 3 (Rotate once. Can rotate a numeric list as well):

// Rotate once. Can rotate a numeric list as well.
MyMap myDigitMap3 = new MyMap(Arrays.asList(38, 56, 98, 160));
System.out.println(myDigitMap3.rotate(1).get()); // {1=160, 2=38, 3=56, 4=98}

Example 4 (Can accept negative rotations):

//Can accept negative rotations.
MyMap myMap = new MyMap(Arrays.asList("Peter", "James", "Sam", "Tiffany", "Mathew"));
System.out.println(myMap.rotate(-3).get()); // {1=Tiffany, 2=Mathew, 3=Peter, 4=James, 5=Sam}
  •  Tags:  
  • Related