How do you organize KeyEvent Input Handler in Java?

Started by
5 comments, last by tom_mai78101 12 years, 6 months ago
Here's a class that I have found that seemed to work, but wastes a lot of memory:

[source lang="java]
package com.mojang.escape;

import java.awt.event.*;

public class InputHandler implements KeyListener, FocusListener, MouseListener, MouseMotionListener {
public boolean[] keys = new boolean[65536];

public void mouseDragged(MouseEvent arg0) {
}

public void mouseMoved(MouseEvent arg0) {
}

public void mouseClicked(MouseEvent arg0) {
}

public void mouseEntered(MouseEvent arg0) {
}

public void mouseExited(MouseEvent arg0) {
}

public void mousePressed(MouseEvent arg0) {
}

public void mouseReleased(MouseEvent arg0) {
}

public void focusGained(FocusEvent arg0) {
}

public void focusLost(FocusEvent arg0) {
for (int i=0; i<keys.length; i++) {
keys = false;
}
}

public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code>0 && code<keys.length) {
keys = true;
}
}

public void keyReleased(KeyEvent e) {
int code = e.getKeyCode();
if (code>0 && code<keys.length) {
keys = false;
}
}

public void keyTyped(KeyEvent arg0) {
}
}
[/source]


Should I really use this design for my input detection class? What should I do with the wasted boolean array? Note that all of the Listeners were implemented for future use. Any suggestions were fine, just that whenever I think of it, it's cringes my mind a lot of OCDness. :blink:


Advertisement
When JVM process starts up, it reserves 64 - 500MB of memory.

Your boolean array is 64kB. Ironically, the above is likely to be fastest solution, all things considered, with exception of clearing the entire array.

But Java comes with a Set class, so why not use that?


public class InputHandler implements KeyListener, FocusListener, MouseListener, MouseMotionListener {
private Set<Integer> keys;

public void focusLost(FocusEvent arg0) {
keys.clear();
} public void keyPressed(KeyEvent e) {
keys.add(e.getKeyCode());
} public void keyReleased(KeyEvent e) {
keys.remove(e.getKeyCode());
} }


However, using a set will consume much more memory over lifetime of application. First the integer from getKeyCode must be boxed into an Integer, creating new instance. Each element added creates another instance. So each add/remove performs two allocations at minimum.

Each of these means there's extra work for GC. It's not a big deal, but for real-time use, allocations tend to be avoided. Obviously, one could code an inline set using an array, considering that typically number of pressed keys will be < 5. Or even if 100 are pressed, one could still save memory.
My program is run at real-time, so this is why I didn't think of using Sets in the first place. Since you said my class is the fastest solution known, I guess it will have to do, and may my OCD rest in peace a little bit.

By inline set initialization, do you mean manually adding the keys in use to the Set, so that when the keys were used, it will just take the keys detected in the Set? Would that be another fast and simple solution, even though I have to cast it to Big Integer?

Since you said my class is the fastest solution known


Not "fastest known". Potentially more efficient given the presented alternatives.

By inline set initialization, do you mean manually adding the keys in use to the Set, so that when the keys were used, it will just take the keys detected in the Set? Would that be another fast and simple solution, even though I have to cast it to Big Integer?[/quote]

I meant this:class KeySet {
private int[] values = new int[16];
private int count;

public void add(int value) {
if (values.length <= count) return;

for (int i = 0; i < count; i++) {
if (values == value) return;
}
values[count] = value;
count++;
}

public void remove(int value) {
if (count == 0) return;
if (values.length <= count) return;

for (int i = 0; i < count; i++) {
if (values == value) {
values = values[count-1];
count--;
return;
}
}
}
};


Given that it's unlikely that 16 keys will be pressed at the same time, the above combines both approaches. Since the array only takes 64 bytes, linear search is sufficient.
Here's a pseudo-code:
[source]
tick()
checksInput()
if (key press detected == true)
key = KeyEvent e.getKeyCode()
KeySet s.add(key)
manipulateInput()
int[] values = KeySet.values
for each (v of values)
if (values.contains(v) == true)
doInputAction(v)
gameTick()
//Do whatever the game wants to do.
[/source]

Other than setting the KeySet.values to be public, do you think I have optimized the manipulateInput() method as best as it can go? Just checking... :unsure:



do you think I have optimized the manipulateInput() method as best as it can go? Just checking...


Ask the profiler. If it was previously the bottleneck and now no longer is, then yes.

[quote name='tom_mai78101' timestamp='1319387546' post='4875628']
do you think I have optimized the manipulateInput() method as best as it can go? Just checking...


Ask the profiler. If it was previously the bottleneck and now no longer is, then yes.
[/quote]

Ok thanks for the help. :D

This topic is closed to new replies.

Advertisement